实验五Elasticsearch+Kibana展示爬虫数据
安装elasticsearch-rtf
Elasticsearch-rtf相比于elasticsearch而言多加了一些插件,因此我们选择安装Elasticsearch-rtf是一个不错的选择。在安装之前我们需要安装java的apk,并且要求版本在8.0或以上,下载地址 https://github.com/medcl/elasticsearch-rtf, 下载完后,进入bin目录 命令行输入
./elasticsearch
即可启动 在浏览器中访问https://127.0.0.1:9200查看结果
安装elasticsearch-head
Elasticsearch-head是一个可视化的管理工具,利用它我们可以清楚的看到elasticsearch中的数据,下载地址 https://github.com/mobz/elasticsearch-head
在终端输入下列命令
git clone git://github.com/mobz/elasticsearch-head.git
cd elasticsearch-head
npm install
npm run start
需要注意的是,在安装前需要测试是否能使用npm命令,npm是nodejs的包管理工具,类似于Java的maven,Python的pip,因此我们需要下载nodejs 下载地址 https://www.nodejs.org 由于npm的中央服务器在国外,速度太慢,因此我们使用淘宝的npm镜像cnpm 下载cnpm命令
$ npm install -g cnpm --registry=https://registry.npm.taobao.org
安装完之后我们需要配置一下config目录下的.yml文件 输入如下四行
http.cors.enabled: true
http.cors.allow-origin: "*"
http.cors.allow-methods: OPTIONS, HEAD, GET, POST, PUT, DELETE
http.cors.allow-headers: "X-Requested-With, Content-Type, Content-Length, X-User"
然后重启一下elasticsearch服务,就可以在9100端口看到elaticsearch连接成功了
安装kibana
下载地址 https://www.elastic.co/downloads/kibana 需要注意的是kibana的版本号必须与elasticsearch对应 我们在面板的info中可以查询elasticsearch版本,当前的版本为5.1.1
下载好对应的版本后,进入bin目录 在终端输入
./kibana
即可运行,在浏览器访问 http://localhost:5601
安装Django
直接使用pip命令安装
pip install django==2.2.2
安装Scrapy
直接使用pip命令安装
pip install scrapy
安装Chromedriver
我们使用Selenium的时候需要用到谷歌浏览器的驱动Chromedriver,下载地址:https://chromedriver.storage.googleapis.com/index.html, 需要找到与谷歌浏览器对应的版本下载,下载完成后放入到/usr/bin或/usr/local/bin目录即可完成安装
sudo mv ./chromedriver /usr/bin
之后在terminal终端输入chromedriver,若显示如下信息,证明安装成功
charles.:~/ $ chromedriver [10:21:40]
Starting ChromeDriver 81.0.4044.138 (8c6c7ba89cc9453625af54f11fd83179e23450fa-refs/branch-heads/4044@{#999}) on port 9515
Only local connections are allowed.
Please protect ports used by ChromeDriver and related test frameworks to prevent access by malicious code.
再通过pip命令安装Selenium
pip install selenium
通过以下代码测试是否可以成功驱动谷歌浏览器
from selenium import webdriver
browser = webdriver.Chrome()
browser.get('http://www.baidu.com')
若可以正常打开浏览器并访问百度首页,证明安装成功
安装MongoDB
在Mac下安装MongoDB可采用Homebrew方式
brew install mongodb
然后创建一个新的文件夹/data/db,用于存放数据
启动MongoDB
brew services start mongodb
sudo mongod
停止和重启MongoDB命令
brew services stop mongodb
brew services restart mongodb
(一)编程实现通用爬虫
无需登录或可抓包模拟登陆
原先我们爬每一个网站都需要单独写一个spider, 这个spider中存在name
,start_urls
,allowed_domains
,rules
等字段,我们通常要完成下面几步:
- 通过scrapy命令新建一个spider
- 定义一个爬取字段信息的字典
- 通过分析网页找到定位规则
- 实现parse()解析字段的函数
而这些步骤相对比较固定,因此我们可以新建一个universal爬虫,通过读取配置 文件的方式来动态生成一个spider,省去了重复创建spider的麻烦
1、在终端启动爬虫命令 在终端输入以下命令,就可以启动爬虫
python3 run.py spider_name
2、读取配置文件,启动爬虫进程 run()函数读取对应spider_name的配置文件后,启动相应的爬虫进程
import sys
from scrapy.utils.project import get_project_settings
from spds.utils import get_config
from scrapy.crawler import CrawlerProcess
def run():
# 获取爬虫名称
name = sys.argv[1]
# 用户配置,一个json文件
custom_settings = get_config(name)
# 使用通用爬虫
spider = custom_settings.get('spider', 'universal')
# 项目配置
project_settings = get_project_settings()
settings = dict(project_settings.copy())
# 合并用户配置和项目配置
settings.update(custom_settings.get('settings'))
# 启动爬虫
process = CrawlerProcess(settings)
process.crawl(spider, **{'name': name})
process.start()
if __name__ == '__main__':
run()
3、run()函数需要调用get_config()方法 读取配置文件的方法为utils.py中的get_config(),它会根 据传入的爬虫名name,去config目录下寻找对应的爬虫的json文件
from os.path import realpath, dirname
import json
def get_config(name):
path = dirname(realpath(__file__)) + '/configs/' + name + '.json'
with open(path, 'r', encoding='utf-8') as f:
return json.loads(f.read())
4、json文件的结构
{"spider": "universal",
"website": "第一财经网",
"type": "新闻",
"index": "http://yicai.com",
"settings": {
"USER_AGENT": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36"
},
"start_urls":{
"type": "static",
"value" :["http://yicai.com/news", "http://yicai.com/data"]
},
"allowed_domains": [
"yicai.com"
],
"rules": "yicai",
"item": {
"class": "NewsItem",
"loader": "ChinaLoader",
"attrs": {
"title": [
{
"method": "xpath",
"args": [
"//div[@class=\"title f-pr\"]/h1//text()"
]
}
],
"url":[
{
"method": "attr",
"args": [
"url"
]
}
],
"website":[
{
"method":"value",
"args":[
"第一财经网"
]
}
]
}
}
}
Selenium + Webdriver
- 信号
由于使用Selenium进行驱动常用的操作无非是滚动、点击、 输入数据的有序组合,那么我就把它们封装成函数,然后通过 信号值分别调用对应的操作,最后只需要规定它们的顺序来完成 用户想要的控制流程.
信号1 滚动
信号2 点击
信号3 输入
所有操作浏览器的流程都封装在一个叫做SeleniumMiddleware
的中间件来实现, 当驱动程序工作完成后,将采集到的数据返回给Spider中的parse_item()
函数 进行解析,最后再通过MongoPipeline
管道存入MongoDB数据库
- 任务
和之前的通用爬虫一样,将所有可配置的参数都抽离出来,形成一个json文件,其中 不同之处在于这里的tasks
字段
"tasks": [
{
"order": 1,
"action": {
"signal": 1,
"args": "",
"text": ""
},
"attrs": {}
},
{
"order": 2,
"action": {
"signal": 2,
"args": "//li[@data-anchor=\"#comment\" and contains(.,\"商品评价\")]"
},
"attrs": {
"mark": "//div[@class=\"comment-percent\"]"
}
},
{
"order": 3,
"action": {
"signal": 2,
"args": "//li[@data-anchor=\"#detail\" and contains(.,\"商品介绍\")]"
},
"attrs": {
"product_name": "//ul[@class=\"parameter2 p-parameter-list\"]/li[contains(.,\"商品名称\")]",
"product_code": "//ul[@class=\"parameter2 p-parameter-list\"]/li[contains(.,\"商品编号\")]",
"product_weight": "//ul[@class=\"parameter2 p-parameter-list\"]/li[contains(.,\"商品毛重\")]"
}
}
]
所有的任务都规定在这个列表中,每个元素是一个字典类型,代表一个task任务,我们规定一个 task任务拥有以下3个属性
- order 用来规定任务task发生的顺序
- action 表示要采取的动作类型,通过signal来确定,每个信号对应的动作见信号
- attrs表示动作结束后所需要获取的字段,它的值是一个字典类型,key为字段名称,值为提取该字段的xpath规则
(二)通过Scrapy框架中的Pipeline将数据写入MongoDB
1、在项目中定义一个MongoPipeline
# pipeline.py
class MongoPipeline():
def __init__(self, mongo_uri, mongo_db, mongo_coll):
self.mongo_uri = mongo_uri
self.mongo_db = mongo_db
self.mongo_coll = mongo_coll
@classmethod
def from_crawler(cls, crawler):
return cls(
mongo_uri=crawler.settings.get('MONGO_URI'),
mongo_db=crawler.settings.get('MONGO_DB'),
mongo_coll=crawler.settings.get('MONGO_COLL')
)
def open_spider(self, spider):
self.client = pymongo.MongoClient(self.mongo_uri)
self.db = self.client[self.mongo_db]
def process_item(self, item, spider):
self.db[self.mongo_coll].insert(dict(item))
return item
def close_spider(self, spider):
self.client.close()
class SpdsPipeline(object):
def process_item(self, item, spider):
return i
2、在settings.py中设定优先顺序
# settings.py
ITEM_PIPELINES = {
# 'spds.pipelines.SpdsPipeline': 300,
'spds.pipelines.MongoPipeline': 301,
}
3、添加MongoDB参数
# MONGO_URI = 'mongodb://jizhu:jizhu123@119.29.214.139:27017' # 腾讯云服务器
MONGO_URI = 'localhost'
MONGO_DB = 'articles'
MONGO_COLL = 'yicai'
(三)搭建本地Elasticsearch集群
其实在Elasticsearch搭建本地集群非常简单,只需要多开几个终端进程,在命令行附带一些额外信息即可
bin/elasticsearch (默认端口9200)
bin/elasticsearch -Ehttp.port=8200 -Epath.data=node2
bin/elasticsearch -Ehttp.port=7200 -Epath.data=node3
通过上面的命令就可以在端口为9200
、8200
和7200
上各开一个Elasticsearch节点
(四)编写程序将MongoDB数据写入Elasticsearch
1、最终的数据需要进行清洗、分词等处理并放入Elasticsearch中进行索引
# lagou_es.py
...
client = pymongo.MongoClient('127.0.0.1', 27017)
db = client['lagou']
collection_list = [ 'jobs', 'quanzhangongchengshi','qukuailian', 'tuxiangchuli', 'tuxiangshibie', 'yuyinshibie']
for coll in collection_list:
print('正在转换::' + coll)
collection = db[coll]
ls = list(collection.find())
for item in ls:
item['website'] = '拉勾网'
job = JobType()
job.website = item['website']
job.url = item.get('url','')
job.job_name = item.get('job_name','')
job.company = item.get('company','')
job.salary = item.get('salary','')
job.city = item.get('city','')
job.experience = item.get('experience','')
job.education = item.get('education','')
job.job_type = item.get('job_type','')
job.publish_time = item.get('publish_time','')
job.job_advantage = item.get('job_advantage','')
job.job_desc = item.get('job_desc','')
job.job_addr = item.get('job_addr','')
job.c_area = item.get('c_area','')
job.c_dev = item.get('c_dev','')
job.c_size = item.get('c_size','')
job.c_investment = item.get('c_investment','')
job.c_homepage = item.get('c_homepage','')
# 新增城市地理位置和工作地地理位置
job.city_location = getGeoPoint(item.get('city',''))
job.job_addr_location = getGeoPoint(item.get('job_addr',''))
job.suggest = gen_suggests(JobType._doc_type.index, ((job.job_name,10),(job.city,3)))
job.save()
...
2、插入Elasticsearch前的额外工作 由于之后需要通过地理信息绘制热力图,需要使用到经纬度信息,所以需要提前将具体的地理位置转换成经纬度信息,这里使用高德地图的地理/逆地理编码API,实现一个getGeoPoint()
方法. (点击这里查看高德API开发文档)
# -*- coding:utf-8 -*-
# Author: Zhu Chen
# Create Time: 2020/06 All rights reserved
import requests
import json
import re
def getGeoPoint(addr):
parameters = {
'key': '84040e9a1a5c324110dbddf00f5d4477',
'address': addr,
'city': '',
'output':'json'
}
url = 'https://restapi.amap.com/v3/geocode/geo'
res = requests.get(url=url, params=parameters)
try:
result = json.loads(res.text).get('geocodes')[0].get('location')
except:
result = ''
# 对经纬度逆置
invert = ''
matched = re.match('(.*),(.*)', result)
if matched:
invert = matched.group(2) + ',' + matched.group(1)
return invert
if __name__ == '__main__':
res = getGeoPoint('北京市朝阳区阜通东大街6号')
print(res)
这里需要注意一点:一般情况下API都是先经度后纬度,但是Elasticseach中Mapping定义的GeoPoint
字段要求的是先纬度后经度,因此需要做一下逆置处理,这一点很容易被人忽略
(五)基于Elasticsearch检索服务实现一个全文搜索引擎
Python Web服务器
配置Django环境,创建项目
1、安装django
pip install django
2、创建目录
mkdir ~/projects/django
3、创建项目
django-admin startproject WebService
4、启动项目
cd WebService
python manage.py runserver
5、创建app
python manage.py startapp Search
项目结构
├── Search
│ ├── __init__.py
│ ├── __pycache__
│ ├── admin.py
│ ├── apps.py
│ ├── migrations
│ ├── models.py
│ ├── urls.py
│ └── views.py
├── WebService
│ ├── __init__.py
│ ├── __pycache__
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── db.sqlite3
├── manage.py
├── static
│ ├── admin
│ ├── css
│ ├── img
│ └── js
└── template
├── index.html
└── result.html
前端功能实现
1、首页搜索提示栏的显示与隐藏
搜索建议是通过Ajax异步请求获取的,关键代码:
// 搜索建议
$(function(){
$('.searchInput').bind(' input propertychange ',function(){
var searchText = $(this).val();
var tmpHtml = ""
$.ajax({
cache: false,
type: 'get',
dataType:'json',
url:suggest_url+"?s="+searchText+"&s_type="+$(".searchItem.current").attr('data-type'),
async: true,
success: function(data) {
for (var i=0;i<data.length;i++){
tmpHtml += '<li><a href="'+search_url+'?q='+data[i]+'">'+data[i]+'</a></li>'
}
$(".dataList").html("")
$(".dataList").append(tmpHtml);
if (data.length == 0){
$('.dataList').hide()
}else {
$('.dataList').show()
}
}
});
} );
})
hideElement($('.dataList'), $('.searchInput'));
2、结果页面的侧边栏显示隐藏
实现代码:
$('.subfieldContext .more').click(function(e){
var $more = $(this).parent('.subfieldContext').find('.more');
if($more.hasClass('show')){
if($(this).hasClass('define')){
$(this).parent('.subfieldContext').find('.more').removeClass('show').find('.text').text('自定义');
}else{
$(this).parent('.subfieldContext').find('.more').removeClass('show').find('.text').text('更多');
}
$(this).parent('.subfieldContext').find('li:gt(2)').hide().end().find('li:last').show();
}else{
$(this).parent('.subfieldContext').find('.more').addClass('show').find('.text').text('收起');
$(this).parent('.subfieldContext').find('li:gt(2)').show();
}
});
$('.sideBarShowHide a').click(function(e) {
if($('#main').hasClass('sideBarHide')){
$('#main').removeClass('sideBarHide');
$('#container').removeClass('sideBarHide');
}else{
$('#main').addClass('sideBarHide');
$('#container').addClass('sideBarHide');
}
});
3、“我的搜索”使用本地缓存
关键代码:
var searchArr;
//定义一个search的,判断浏览器有无数据存储(搜索历史)
if(localStorage.search){
//如果有,转换成 数组的形式存放到searchArr的数组里(localStorage以字符串的形式存储,所以要把它转换成数组的形式)
searchArr= localStorage.search.split(",")
}else{
//如果没有,则定义searchArr为一个空的数组
searchArr = [];
}
//把存储的数据显示出来作为搜索历史
MapSearchArr();
function add_search(){
var val = $(".searchInput").val();
if (val.length>=2){
//点击搜索按钮时,去重
KillRepeat(val);
//去重后把数组存储到浏览器localStorage
localStorage.search = searchArr;
//然后再把搜索内容显示出来
MapSearchArr();
}
window.location.href=search_url+'?q='+val+"&s_type="+$(".searchItem.current").attr('data-type')
}
4、关键词去重处理
关键代码:
function KillRepeat(val){
var kill = 0;
for (var i=0;i<searchArr.length;i++){
if(val===searchArr[i]){
kill ++;
}
}
if(kill<1){
searchArr.unshift(val);
}else {
removeByValue(searchArr, val)
searchArr.unshift(val)
}
}
后端功能实现
1、View视图层的部分实现代码:
class IndexView(View):
#首页
def get(self, request):
_topn_search = redis_kw.zrevrangebyscore("search_keywords_set", "+inf", "-inf", start=0, num=5)
topn_search = []
for item in _topn_search:
topn_search.append(item.decode())
return render(request, "index.html", {"topn_search":topn_search})
#return render(request, "index.html")
class SearchSuggest(View):
#搜索建议功能
def get(self, request):
key_words = request.GET.get('s','')
re_datas = []
if key_words:
s = ArticleType.search()
s = s.suggest('my_suggest', key_words, completion={
"field":"suggest", "fuzzy":{
"fuzziness":2
},
"size": 10
})
suggestions = s.execute_suggest()
for match in suggestions.my_suggest[0].options:
source = match._source
re_datas.append(source["title"])
return HttpResponse(json.dumps(re_datas), content_type="application/json")
class SearchView(View):
#结果页
def get(self, request):
key_words = request.GET.get("q","")
s_type = request.GET.get("s_type", "article")
redis_kw.zincrby("search_keywords_set", 1, key_words)
_topn_search = redis_kw.zrevrangebyscore("search_keywords_set", "+inf", "-inf", start=0, num=5)
topn_search = []
for item in _topn_search:
topn_search.append(item.decode())
page = request.GET.get("p", "1")
try:
page = int(page)
except:
page = 1
try:
yicai_count = redis_cnt.get("articles").decode()
except:
yicai_count = 9999
start_time = datetime.now()
response = client.search(
index= "yicai_new",
body={
"query":{
"multi_match":{
"query":key_words,
"fields":["intro", "title", "contents"]
}
},
"from":(page-1)*10,
"size":10,
"highlight": {
"pre_tags": ['<span class="keyWord">'],
"post_tags": ['</span>'],
"fields": {
"title": {},
"contents": {},
"intro":{}
}
}
}
)
end_time = datetime.now()
last_seconds = (end_time-start_time).total_seconds()
total_nums = response["hits"]["total"]
if (page%10) > 0:
page_nums = int(total_nums/10) +1
else:
page_nums = int(total_nums/10)
hit_list = []
for hit in response["hits"]["hits"]:
hit_dict = {}
if "title" in hit["highlight"]:
hit_dict["title"] = "".join(hit["highlight"]["title"])
else:
hit_dict["title"] = hit["_source"]["title"]
if "contents" in hit["highlight"]:
hit_dict["contents"] = "".join(hit["highlight"]["contents"])[:1000]
else:
hit_dict["contents"] = hit["_source"]["contents"][:1000]
hit_dict["time"] = hit["_source"]["time"]
hit_dict["url"] = hit["_source"]["url"]
hit_dict["score"] = hit["_score"]
hit_dict["source"] = hit["_source"]["source"]
hit_dict["website"] = hit["_source"]["website"]
hit_list.append(hit_dict)
return render(request, "result.html", {"page":page,
"all_hits":hit_list,
"key_words":key_words,
"total_nums":total_nums,
"page_nums":page_nums,
"last_seconds":last_seconds,
"yicai_count":yicai_count,
"topn_search":topn_search
})
2、Model层的部分实现代码:
class ArticleType(DocType):
#第一财经文章类型
suggest = Completion(analyzer=ik_analyzer)
title = Text(analyzer="ik_max_word")
url = Keyword()
time = Date()
author = Keyword()
intro = Text(analyzer='ik_max_word')
source = Keyword()
contents = Text(analyzer='ik_max_word')
editor = Keyword()
website = Keyword()
class Meta:
index = "yicai"
doc_type = "article"
3、Url路由的配置代码:
urlpatterns = [
path('', IndexView.as_view(), name='index'),
path('suggest/', SearchSuggest.as_view(), name='suggest'),
path('search/', SearchView.as_view(), name='search'),
]
4、全局路由配置urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('', include(app_urls))
]
(六)用Kibana实现数据分析和数据可视化
DevTools
Elasticsearch 提供了 REST HTTP 接口,对于开发和测试也极其方便,在实际使用时,很多人会使用命令行 curl 或者 Postman之类图形化 http 请求工具来完成对 Elasticsearch 的请求。官方有 Kibana中的Dev Tools Console插件这类神兵利器,用来替代效率低下的命令行,用过就再也回不去命令行了
Mapping 映射
为了能够将时间域视为时间,数字域视为数字,字符串域视为全文或精确值字符串, Elasticsearch 需要知道每个域中数据的类型。这个信息包含在映射中。
Elasticsearch 支持如下简单域类型:
- 字符串: string
- 整数 : byte, short, integer, long
- 浮点数: float, double
- 布尔型: boolean
- 日期: date
Elasticsearch还提供了一些高级的类型,比如Elasticsearch提供了 两种表示地理位置的方式:用纬度-经度表示的坐标点使用 geo_point
字段类型, 以 GeoJSON 格式定义的复杂地理形状,使用 geo_shape
字段类型。我们在绘制热点城市地图的时候定义的job_addr_location
使用的就是geo_point
类型
Aggregation 聚合
聚合允许我们向数据提出一些复杂的问题。虽然功能完全不同于搜索,但它使用相同的数据结构。这意味着聚合的执行速度很快并且就像搜索一样几乎是实时的。
这对报告和仪表盘是非常强大的。它实时显示客户数据,便于立即回应,而不是对数据进行汇总( 需要一周时间去运行的 Hadoop 任务 ),报告随着数据变化而变化,而不是预先计算的、过时的和不相关的。
聚合的两个主要的概念:
- 桶(Buckets) 满足特定条件的文档的集合
- 指标(Metrics) 对桶内的文档进行统计计算
聚合实例:
汽车经销商可能会想知道哪个颜色的汽车销量最好,用聚合可以轻易得到结果,用 terms 桶操作:
实验五Elasticsearch+Kibana展示爬虫数据的更多相关文章
- Nginx filebeat+logstash+Elasticsearch+kibana实现nginx日志图形化展示
filebeat+logstash+Elasticsearch+kibana实现nginx日志图形化展示 by:授客 QQ:1033553122 测试环境 Win7 64 CentOS-7- ...
- kafka日志同步至elasticsearch和kibana展示
kafka日志同步至elasticsearch和kibana展示 一 kafka consumer准备 前面的章节进行了分布式job的自动计算的概念讲解以及实践.上次分布式日志说过日志写进kafka, ...
- 手把手教你写带登录的NodeJS爬虫+数据展示
其实在早之前,就做过立马理财的销售额统计,只不过是用前端js写的,需要在首页的console调试面板里粘贴一段代码执行,点击这里.主要是通过定时爬取https://www.lmlc.com/s/web ...
- 第三百六十七节,Python分布式爬虫打造搜索引擎Scrapy精讲—elasticsearch(搜索引擎)scrapy写入数据到elasticsearch中
第三百六十七节,Python分布式爬虫打造搜索引擎Scrapy精讲—elasticsearch(搜索引擎)scrapy写入数据到elasticsearch中 前面我们讲到的elasticsearch( ...
- 四十六 Python分布式爬虫打造搜索引擎Scrapy精讲—elasticsearch(搜索引擎)scrapy写入数据到elasticsearch中
前面我们讲到的elasticsearch(搜索引擎)操作,如:增.删.改.查等操作都是用的elasticsearch的语言命令,就像sql命令一样,当然elasticsearch官方也提供了一个pyt ...
- 爬虫数据存储——安装docker和ElasticSearch(基于Centos7)
爬虫数据存储--安装docker和ElasticSearch(基于Centos7) 先决条件 操作系统要求 要安装Docker Engine-Community,您需要一个CentOS 7的维护版本. ...
- syslog+rsyslog+logstash+elasticsearch+kibana搭建日志收集
最近rancher平台上docker日志收集捣腾挺久的,尤其在配置上,特写下记录 Unix/Linux系统中的大部分日志都是通过一种叫做syslog的机制产生和维护的.syslog是一种标准的协议,分 ...
- Helm3 安装 ElasticSearch & Kibana 7.x 版本
文章转载自:http://www.mydlq.club/article/13/ 系统环境: helm 版本:v3.2.1 Kubernetes 版本:1.18.3 ElasticSearch Char ...
- Linux基础入门(新版)(实验五至实验八)
实验五 环境变量与文件查找 (环境变量的作用与用法,及几种搜索文件的方法) 一.环境变量 1.变量 (1)常变量与值是一对一的关系 (2)变量的作用域即变量的有效范围(比如一个函数中.一个源文 ...
- python爬虫主要就是五个模块:爬虫启动入口模块,URL管理器存放已经爬虫的URL和待爬虫URL列表,html下载器,html解析器,html输出器 同时可以掌握到urllib2的使用、bs4(BeautifulSoup)页面解析器、re正则表达式、urlparse、python基础知识回顾(set集合操作)等相关内容。
本次python爬虫百步百科,里面详细分析了爬虫的步骤,对每一步代码都有详细的注释说明,可通过本案例掌握python爬虫的特点: 1.爬虫调度入口(crawler_main.py) # coding: ...
随机推荐
- kafka详解(01) - 概述
kafka详解(01) - 概述 定义:Kafka是一个分布式的基于发布/订阅模式的消息队列(Message Queue),主要应用于大数据实时处理领域. 消息队列 MQ传统应用场景之异步处理 使用消 ...
- Spring 和 Spring MVC的区别
Spring 和 Spring MVC的区别 学习Spring MVC也有几天时间了,那么Spring和Spring MVC的区别到底在哪里,二者是什么关系呢?认为二者是一个东西那肯定是不对的,而 ...
- Miller-Rabin 与 Pollard-Rho 算法学习笔记
前言 Miller-Rabin 算法用于判断一个数 \(p\) 是否是质数,若选定 \(w\) 个数进行判断,那么正确率约是 \(1-\frac{1}{4^w}\) ,时间复杂度为 \(O(\log ...
- 今天学到的新知识--自己的电脑可以像Github Pages、码云 Pages一样发布静态资源
大佬教我的,感觉这个很神奇哦 假设下面这个路径是我的本地电脑静态资源路径 打开powershell窗口 然后按照下图的样子执行命令 复制网址就可以访问啦 然后可以通过 https://iplocati ...
- 当LOGO设计与世界文化擦出火花——JJQ的LOGO设计之路
<当LOGO设计与世界文化碰撞出火花--论 JJQ 的LOGO是如何制成的> (友链:https://tg.hszxoj.com/user/475) 首先我们对jjq对应的汉字进行拉长 ...
- 让Apache Beam在GCP Cloud Dataflow上跑起来
简介 在文章<Apache Beam入门及Java SDK开发初体验>中大概讲了Apapche Beam的简单概念和本地运行,本文将讲解如何把代码运行在GCP Cloud Dataflow ...
- C#如何提高代码质量(二)
多线程,异步,任务和并行 1.异步和多线程应用场景区分 多线程 计算密集型工作 异步 IO密集型工作 2.线城同步中使用信号量 EventWaitHandle AutoResetEvent Manua ...
- 接水问题(NOIP 2010 PJT2)
这个的思路就是让各个水龙头所用的时间尽可能地接近,可以先向优先队列中推入前m个数,由于开的是小根堆最小的数在前面我们把它拿出来,加上下一个人所需的时间.如此反复,直到都接完水,最大值就是答案. #in ...
- 2211-11Flask入门教程
本篇记录来自Flask入门教程 准备工作 在通过这本书学习 Flask 开发前,我假设你已经了解了 Python 和 HTML 的基础知识.如果还没有,那么可以先从下面这些在线资源入手: <使用 ...
- 手写一个audio播放器,实现歌曲切换,列表歌曲循环,音量调节等 vue组件
1 <template> 2 <div class="wrapper"> 3 <svg 4 t="1673833915638" 5 ...