共计 17997 个字符,预计需要花费 45 分钟才能阅读完成。
作者:京东物流 田禹
1 网络爬虫
网络爬虫:是一种依照肯定的规定,主动地抓取万维网信息的程序或者脚本。
网络爬虫相干技术和框架繁多,针对场景的不同能够抉择不同的网络爬虫技术。
2 Scrapy 框架(Python)
2.1. Scrapy 架构
2.1.1. 零碎架构
2.1.2. 执行流程
总结爬虫开发过程,简化爬虫执行流程如下图所示:
爬虫运行次要流程如下:
(1)Scrapy 启动 Spider 后加载 Spaider 的 start_url,生成 request 对象;
(2)通过 middleware 欠缺 request 对象(增加 IP 代理、User-Agent);
(3)Downloader 对象依照 request 对象下载页面;
(4)将 response 后果传递给 spider 的 parser 办法解析;
(5)spider 获取数据封装为 item 对象传递给 pipline,解析的 request 对象将返回调度器进行新一轮的数据抓取;
2.2. 框架外围文件介绍
2.2.1. scrapy.cfg
scrapy.cfg 是 scrapy 框架的入口文件,settings 节点指定爬虫的配置信息,deploy 节点用于指定 scrapyd 服务的部署门路。
|
[settings]
default = sfCrawler.settings
[deploy]
url =http://localhost:6800/
project = jdCrawler
|
2.2.2. settings.py
settings 次要用于配置爬虫启动信息,包含:并发线程数量、应用的 middleware、items 等信息;也能够作为零碎中的全局的配置文件应用。
注:目前次要减少了 redis、数据库连贯等相干配置信息。
2.2.3. middlewares.py
middleware 定义了多种接口,别离在爬虫加载、输出、输入、申请、申请异样等状况进行调用。
注:目前次要用户是为爬虫减少 User-Agent 信息和 IP 代理信息等。
2.2.4. pipelines.py
用于定义解决数据的 Pipline 对象,scrapy 框架能够在 settings.py 文件中配置多个 pipline 对象,解决数据的个过程将依照 settings.py 配置的优先级的程序依次执行。
注:零碎中产生的每个 item 对象,将通过 settings.py 配置的所有 pipline 对象。
2.2.5. items.py
用于定义不同种数据类型的数据字典,每个属性都是 Field 类型;
2.2.6. spider 目录
用于寄存 Spider 子类定义,scrapy 启动爬虫过程中将依照 spider 类中 name 属性进行加载和调用。
2.3. 爬虫性能扩大阐明
2.3.1. user\_agents\_middleware.py
通过 procces_request 办法,为 request 对象增加 hearder 信息,随机模仿多种浏览器的 User-Agent 信息进行网络申请。
2.3.2. proxy_server.py
通过 procces_request 办法,为 reques 对象增加网络代理信息,随机模仿多 IP 调用。
2.3.3. db\_connetion\_pool.py
文件地位
db\_manager/db\_connetion_pool.py,文件定义了根底的数据连接池,不便零碎各环节操作数据库。
2.3.4. redis\_connention\_pool.py
文件地位 db\_manager/ redis\_connention_pool.py,文件定义了根底的 Redis 连接池,不便零碎各环节操作 Redis 缓存。
2.3.5. scrapy_redis 包
scrapy_redis 包是对 scrapy 框架的扩大,采纳 Redis 作为申请队列,存储爬虫工作信息。
spiders.py 文件:定义分布式 RedisSpider 类,通过笼罩 Spider 类 start\_requests()办法的形式,从 Redis 缓存中获取初始申请列表信息。其中 RedisSpider 子类须要为 redis\_key 赋值。
pipelines.py 文件:定义了一种简略的数据存储形式,能够间接将 item 对象序列化后保留到 Redis 缓存中。
dupefilter.py 文件:定义数据去重类,采纳 Redis 缓存的形式,曾经保留的数据将增加到过滤队列中。
queue.py 文件:定义几种不同的入队和出队程序的队列,队列采纳 Redis 存储。
2.4. 微博爬虫开发示例
2.4.1. 查找爬虫入口
2.4.1.1. 站点剖析
网站个别会分为 Web 端和 M 端两种,两种站点在设计和架构上会有较大的差异。通常状况下 Web 端会比拟成熟,User-Agent 查看、强制 Cookie、登录跳转等限度,抓取难度绝对较大,返回后果以 HTML 内容为主;M 端站点通常采纳前后端拆散设计,大多提供独立的数据接口。所以站点剖析过程中优先查找 M 端站点入口。微博 Web 端及 M 端成果如图所示:
微博 Web 端地址:https://weibo.com/,页面显示成果如下图所示:
注:图片来源于微博 PC 端截图
微博 M 端地址:https://m.weibo.cn/?jumpfrom=weibocom,页面显示成果如下图所示:
注:图片来源于微博 M 端截图
2.4.1.2. HTML 源码剖析
Web 端站点和 M 端站点返回后果都是 HTML 格局,局部站点为了晋升页面渲染速度,或者为了减少代码剖析难度,通过动静 JavaScrip 执行等形式,动静生成 HTML 页面,网络爬虫短少 JS 执行和渲染过程,很难获取实在的数据,微博 Web 端站点 HTML 代码片段如下所示:
脚本中的注释内容:
M 端站点 HTML 内容:
M 端 HTML 内容中并未呈现页面中的要害信息,能够断定为前后端拆散的设计形式,通过 Chrome 浏览器开发模式,可能查看所有申请信息,通过申请的类型和返回后果,根本能够确定接口地址,查找过程如下图所示:
注:图片来源于微博 M 端截图
(1)关上 Chrome 开发者工具,刷新以后页面;
(2)批改申请类型为 XHR,筛选 Ajax 申请;
(3)查看所有申请信息,疏忽没有返回后果的接口;
(4)在接口返回后果中查找页面中相干内容。
2.4.1.3. 接口分析
接口分析次要包含:申请地址剖析、申请形式、参数列表、返回后果等。
申请地址、申请形式和参数列表能够依据 Chrome 开发人员工具中的网络申请 Header 信息获取,申请信息如下图所示:
上图中接口地址采纳的是 GET 形式申请,申请地址是 unicode 编码,参数内容能够查看 Query String Parameters 列表查看申请参数,成果如下图所示:
申请后果剖析次要剖析数据结构的特点,查找与注释内容雷同的数据结构,同时要查看所有后果是否与注释内容统一,防止非凡返回后果影响数据解析过程。
2.4.1.4. 接口验证
接口验证个别须要两个步骤:
(1)用浏览器(最好是新开浏览器,如 Chrome 的隐身模式)模仿申请过程,在地址栏中输出带有参数的申请地址查看返回后果。
(2)采纳 Postman 等工具模仿浏览器申请过程,次要模仿非 Get 形式的网络申请,同样也能够验证站点是否强制应用 Cookie 和 User-Agent 信息等。
2.4.2. 定义数据结构
爬虫数据结构定义次要联合业务需要和数据抓取的后果进行设计,微博数据次要用户国内的舆情零碎,所以在开发过程中将相干站点的数据对立定义为 OpinionItem 类型,在不同站点的数据保留过程中,依照 OpinionItem 数据结构的特点拆卸数据。在 items.py 文件中定义舆情数据结构如下所示:
class OpinionItem(Item):
rid = Field()
pid = Field()
response_content = Field() # 接口返回的全副信息
published_at = Field() # 公布工夫
title = Field() # 题目
description = Field() # 形容
thumbnail_url = Field() # 缩略图
channel_title = Field() # 频道名称
viewCount = Field() # 观看数
repostsCount = Field() # 转发数
likeCount = Field() # 点赞数
dislikeCount = Field() # 不喜爱数量
commentCount = Field() # 评论数
linked_url = Field() # 链接
updateTime = Field() # 更新工夫
author = Field() # 作者
channelId = Field() # 渠道 ID
mediaType = Field() # 媒体类型
crawl_time = Field() # 抓取工夫
type = Field() # 信息类型:1 主贴 2 主贴的评论
2.4.3. 爬虫开发
微博爬虫采纳分布式 RedisSpider 作为父类,爬虫定义如下所示:
class weibo_list(RedisSpider):
name = 'weibo'
allowed_domains = ['weibo.cn']
redis_key = 'spider:weibo:list:start_urls'
def parse(self, response):
a = json.loads(response.body)
b = a['data']['cards']
for j in range(len(b)):
bb = b[j]
try:
for c in bb['card_group']:
try:
d = c['mblog']
link = 'https://m.weibo.cn/api/comments/show?id={}'.format(d['mid'])
# yield scrapy.Request(url=link, callback=self.parse_detail)
# 内容解析代码片段
opinion['mediaType'] = 'weibo'
opinion['type'] = '1'
opinion['crawl_time'] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
yield opinion
except Exception as e:
print(e)
continue
except Exception as e:
print(e)
continue
代码解析:
代码行 1:定义 weibo_list 类,继承 RedisSpider 类;
代码行 2:定义爬虫名称,爬虫启动时应用;
代码行 3:减少容许拜访的地址域名列表;
代码行 4:定义微博开始申请地址的 redis key;
代码行 6:定义爬虫的解析办法,爬虫下载页面后默认调用 parser 办法;
代码行 7~21:将下载后果内容解析后拆卸到 item 对象中;
代码行 22:通过 yield 关键字,生成 Python 特有的生成器对象,调用者能够通过生成器对象遍历所有后果。
2.4.4. 数据存储
数据存储次要通过定义 Pipline 实现类,将爬虫解析的数据进行保留。微博数据须要减少进行情感剖析的加工过程,开发过程中首先将微博数据保留到 Redis 服务中,通过后续的情感剖析解析服务获取、加工,并保留到数据库中。数据保留代码如下所示:
class ShunfengPipeline(object):
def process_item(self, item, spider):
if isinstance(item, OpinionItem):
try:
print('===========Weibo 查问后果 ============')
key = 'spider:opinion:data'
dupe = 'spider:opinion:dupefilter'
attr_list = []
for k, v in item.items():
if isinstance(v, str):
v = v.replace('\'', '\\\'')
attr_list.append("%s:'%s'" % (k, v))
data = ",".join(attr_list)
data = "{%s}" % data
# 依照数据起源、类型和惟一 id 作为去重标识
single_key = ''.join([item['mediaType'], item['type'], item['rid']])
if ReidsPool().rconn.execute_command('SADD', dupe, single_key) != 0:
ReidsPool().rconn.execute_command('RPUSH', key, data)
except Exception as e:
print(e)
pass
return item
要害代码阐明:
代码行 1:定义 Pipline 类;
代码行 2:定义 process_item 办法,用于接收数据;
代码行 3:依照 Item 类型别离解决;
代码行 4~17:Item 对象拼装为 JSON 字符串;
代码行 18~21:对数据进行去重校验,并将数据保留到 redis 队列中;
代码行 26:返回 item 对象供其余 Pipline 操作;
Pipline 定义实现后,须要在工程中的 settings.py 文件中进行配置,配置内容如下所示:
|
\# 配置我的项目管道
ITEM_PIPELINES = {
“sfCrawler.pipelines.JdcrawlerPipeline”: 401,
“sfCrawler.pipelines\_manage.mysql\_pipelines.MySqlPipeline”: 402,
“sfCrawler.pipelines\_manage.shunfeng\_pipelines.ShunfengPipeline”: 403,
}
|
爬虫入门地址:
https://scrapy-chs.readthedocs.io/zh_CN/0.24/intro/tutorial.html
3 WebMagic 框架(Java)
3.1 前言
总结 Scrapy 应用过程中存在的问题,以及爬虫零碎前期上线需要思考,采纳 Java 语言进行爬虫的设计与开发。具体起因如下:
(1)上线根底环境依赖:须要应用线上 Clover、JimDB、MySQL 等根底环境;
(2)可扩展性强:以现有框架为根底,二次封装 Request 申请对象,实现通用网络爬虫开发,提供易扩大的地址生成、网页解析接口。
(3)集中部署:通过部署通用爬虫形式,抓取所有反对站点的爬虫,解决 Scrapy 框架一站点一部署问题。
(4)反 - 反爬虫:局部站点针对 Scrapy 框架申请特点,施行反爬虫策略(如:天猫),回绝所有爬虫申请。WebMagic 模仿浏览器申请,不受该爬虫限度。
3.2 WebMagic 概述
(内容起源:https://webmagic.io/docs/zh/posts/ch1-overview/architecture.html)
3.2.1 总体架构
WebMagic 的构造分为 Downloader、PageProcessor、Scheduler、Pipeline 四大组件,并由 Spider 将它们彼此组织起来。这四大组件对应爬虫生命周期中的下载、解决、治理和长久化等性能。WebMagic 的设计参考了 Scapy,然而实现形式更 Java 化一些。
而 Spider 则将这几个组件组织起来,让它们能够相互交互,流程化的执行,能够认为 Spider 是一个大的容器,它也是 WebMagic 逻辑的外围。
WebMagic 总体架构图如下:
3.1.2 WebMagic 的四个组件
3.1.2.1 Downloader
Downloader 负责从互联网上下载页面,以便后续解决。WebMagic 默认应用了 Apache HttpClient 作为下载工具。
3.1.2.2 PageProcessor
PageProcessor 负责解析页面,抽取有用信息,以及发现新的链接。WebMagic 应用 Jsoup 作为 HTML 解析工具,并基于其开发了解析 XPath 的工具 Xsoup。
在这四个组件中,PageProcessor 对于每个站点每个页面都不一样,是须要使用者定制的局部。
3.1.2.3 Scheduler
Scheduler 负责管理待抓取的 URL,以及一些去重的工作。WebMagic 默认提供了 JDK 的内存队列来治理 URL,并用汇合来进行去重。也反对应用 Redis 进行分布式治理。
除非我的项目有一些非凡的分布式需要,否则无需本人定制 Scheduler。
3.1.2.4 Pipeline
Pipeline 负责抽取后果的解决,包含计算、长久化到文件、数据库等。WebMagic 默认提供了“输入到控制台”和“保留到文件”两种后果解决计划。
Pipeline 定义了后果保留的形式,如果你要保留到指定数据库,则须要编写对应的 Pipeline。对于一类需要个别只需编写一个 Pipeline。
3.1.3 用于数据流转的对象
3.1.3.1 Request
Request 是对 URL 地址的一层封装,一个 Request 对应一个 URL 地址。
它是 PageProcessor 与 Downloader 交互的载体,也是 PageProcessor 管制 Downloader 惟一形式。
除了 URL 自身外,它还蕴含一个 Key-Value 构造的字段 extra。你能够在 extra 中保留一些非凡的属性,而后在其余中央读取,以实现不同的性能。例如附加上一个页面的一些信息等。
3.1.3.2 Page
Page 代表了从 Downloader 下载到的一个页面——可能是 HTML,也可能是 JSON 或者其余文本格式的内容。
Page 是 WebMagic 抽取过程的外围对象,它提供一些办法可供抽取、后果保留等。在第四章的例子中,咱们会具体介绍它的应用。
3.1.3.3 ResultItems
ResultItems 相当于一个 Map,它保留 PageProcessor 解决的后果,供 Pipeline 应用。它的 API 与 Map 很相似,值得注意的是它有一个字段 skip,若设置为 true,则不应被 Pipeline 解决。
3.1.4 管制爬虫运行的引擎 –Spider
Spider 是 WebMagic 外部流程的外围。Downloader、PageProcessor、Scheduler、Pipeline 都是 Spider 的一个属性,这些属性是能够自在设置的,通过设置这个属性能够实现不同的性能。Spider 也是 WebMagic 操作的入口,它封装了爬虫的创立、启动、进行、多线程等性能。上面是一个设置各个组件,并且设置多线程和启动的例子。具体的 Spider 设置请看第四章——爬虫的配置、启动和终止。
public static void main(String[] args) {Spider.create(new GithubRepoPageProcessor())
// 从 https://github.com/code4craft 开始抓
.addUrl("https://github.com/code4craft")
// 设置 Scheduler,应用 Redis 来治理 URL 队列
.setScheduler(new RedisScheduler("localhost"))
// 设置 Pipeline,将后果以 json 形式保留到文件
.addPipeline(new JsonFilePipeline("D:\\data\\webmagic"))
// 开启 5 个线程同时执行
.thread(5)
// 启动爬虫
.run();}
3.3 通用爬虫剖析及设计
3.2.1 通用爬虫功能分析
(1)单个利用同时反对多个站点的数据抓取;
(2)反对集群部署;
(3)容易扩大;
(4)反对反复抓取;
(5)反对定时抓取;
(6)具备大数据分析扩大能力;
(7)升高大数据分析集成复杂度,进步代码复用性;
(8)反对线上部署;
3.2.2 通用爬虫设计
通用爬虫设计思路是在 WebMagic 根底上,将 Scheduler、Processor、Pipeline 进行定制,设计如下图所示:
设计过程中依照爬虫开发的特点,将爬虫实现过程拆分为两个环节:生成申请和内容解析。
(1)生成申请(UrlParser):依照不同站点的申请地址及参数特点(如:Get/Post 申请形式,URL 参数拼接等)及业务须要(如:应用国内代理还是国外代理),依照站点参数拼装成为通用的 Request 申请对象,领导 Downloader 进行网页下载。
(2)内容解析(HtmlParser):依照解析网页内容的特点,通过 XPATH、JSON 等形式对页面内容进行提取过程,每个内容解析器只针对雷同页面内容进行解析,当内容中蕴含深度页面抓取的申请时,由 UrlParser 生成新的申请对象并返回给调度器。
3.2.3 任务调度设计
为了实现爬虫的分布式,将 Scheduler 性能进行弱化,实现过程减少 Clover、Worker、Redis 环节。Clover 负责定时调度 Worker 生成默认申请对象(个别为检索性能、首页反复抓取工作等),将生成的申请对象增加到 Redis 队列中,Scheduler 只负责从 Redis 队列中获取申请地址即可。
3.2.4 Processor 设计
Processor 用于解析 Downloader 下载的网页内容,为了充分利用服务器网络和计算资源,设计之初思考可能将网页下载和内容解析拆分到不同服务中进行解决,防止爬虫节点 CPU 工夫过长造成网络带宽的节约。所以,设计过程中依照爬虫外部解析和内部平台集成解析两种形式进行设计。
(1)爬虫外部解析:即下载内容间接由 Processor 实现页面解析过程,生成 Items 对象和深度 Request 对象。为了简化对多个站点内容的解析过程,Processor 次要负责数据结构的组织和 HtmlParser 的调用,通过 Spring IOC 的形式实现多个站点 HtmlParser 集成过程。
(2)内部平台集成:可能将爬虫抓取内容通过 MQ 等形式,实现其余平台或服务的对接过程。实现过程中能够将抓取的网页内容组织成为文本类型,通过 Pipline 形式将数据发送到 JMQ 中,依照 JMQ 形式实现与其余服务和平台的对接过程。其余服务能够复用 HtmlParser 和 UrlParser 实现内容解析过程。
3.2.5 Pipline 设计
Pipline 次要用于数据的转储,为了实用 Processor 的两种计划,设计 MySQLPipeline 和 JMQPipeline 两种实现形式。
3.4 通用爬虫实现
3.4.1 Request
WebMagic 提供的 Request 类可能满足网络申请的根本需要,包含 URL 地址、申请形式、Cookies、Headers 等信息。为了实现通用的网络申请,对现有申请对象进行业务扩大,减少是否过滤、过滤 token、申请头类型(PC/APP/WAP)、代理 IP 国别分类、失败重试次数等。扩大内容如下:
/**
* 站点
*/
private String site;
/**
* 类型
*/
private String type;
/**
* 是否过滤 default:TRUE
*/
private Boolean filter = Boolean.TRUE;
/**
* 惟一 token,URL 地址去重应用
*/
private String token;
/**
* 解析器名称
*/
private String htmlParserName;
/**
* 是否填充 Header 信息
*/
private Integer headerType = HeaderTypeEnums.NONE.getValue();
/**
* 国别类型,用于辨别应用代理类型
* 默认为国内
*/
private Integer nationalType = NationalityEnums.CN.getValue();
/**
* 页面最大抓取深度,用于限度列表页深度钻取深度,依照拜访次数顺次递加
* <p>Default: 1</p>
* <p> depth = depth - 1</p>
*/
private Integer depth = 1;
/**
* 失败重试次数
*/
private Integer failedRetryTimes;
3.4.2 UrlParser & HtmlParser
3.4.2.1 UrlParser 实现
UrlParser 次要用与依照参数列表生成固定的申请对象,为了简化 Workder 的开发过程,在接口中减少了生成初始化申请的办法。
/**
* URL 地址转换
* @author liwanfeng1
*/
public interface UrlParser {
/**
* 获取定时工作初始化申请对象列表
* @return 申请对象列表
*/
List<SeparateRequest> getStartRequest();
/**
* 依照参数生成 Request 申请对象
* @param params
* @return
*/
SeparateRequest parse(Map<String, Object> params);
}
3.4.2.2 HtmlParser 实现
HtmlParser 次要是将 Downloader 内容进行解析,并返回 data 列表及深度抓取的 Request 对象。实现如下:
/**
* HTML 代码转换
* @author liwanfeng1
*/
public interface HtmlParser {
/**
* HTML 格式化
* @param html 抓取的网页内容
* @param request 网络申请的 Request 对象
* @return 数据解析后果
*/
HtmlDataEntity parse(String html, SeparateRequest request);
}
/**
* @author liwanfeng1
* @param <T> 数据类型
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class HtmlDataEntity<T extends Serializable> {
private List<T> data;
private List<SeparateRequest> requests;
/**
* 增加数据对象
* @param obj 数据对象
*/
public void addData(T obj){if(data == null) {data = new ArrayList<>();
}
data.add(obj);
}
/**
* 增加 Request 对象
* @param request 申请对象
*/
public void addRequest(SeparateRequest request) {if(requests == null) {requests = new ArrayList<>();
}
requests.add(request);
}
}
3.4.3 Worker
Workder 的作用是定时生成申请对象,联合 UrlParser 接口的利用,设计对立的 WorkerTask 实现类,代码如下:
/**
*
* @author liwanfeng1
*/
@Slf4j
@Data
public class CommonTask extends AbstractScheduleTaskProcess<SeparateRequest> {
private UrlParser urlParser;
private SpiderQueue spiderQueue;
/**
* 获取工作列表
* @param taskServerParam 参数列表
* @param i 编号
* @return 工作列表
*/
@Override
protected List<SeparateRequest> selectTasks(TaskServerParam taskServerParam, int i) {return urlParser.getStartRequest();
}
/**
* 执行工作列表,组织 Google API 申请地址,增加到 YouTube 列表爬虫的队列中
* @param list 工作列表
*/
@Override
protected void executeTasks(List<SeparateRequest> list) {spiderQueue.push(list);
}
}
增加 Worker 配置如下:
<!-- Facebook start -->
<bean id="facebookTask" class="com.jd.npoms.worker.task.CommonTask">
<property name="urlParser">
<bean class="com.jd.npoms.spider.urlparser.FacebookUrlParser"/>
</property>
<property name="spiderQueue" ref="jimDbQueue"/>
</bean>
<jsf:provider id="facebookTaskProcess"
interface="com.jd.clover.schedule.IScheduleTaskProcess" ref="facebookTask"
server="jsf" alias="woker:facebookTask">
</jsf:provider>
<!-- Facebook end -->
3.4.4 Scheduler
调度次要用于推送、拉取最新工作,以及辅助的推送反复验证、队列长度获取等办法,接口设计如下:
/**
* 爬虫工作队列
* @author liwanfeng
*/
public interface SpiderQueue {
/**
* 增加队列
* @param params 爬虫地址列表
*/
default void push(List<SeparateRequest> params) {if (params == null || params.isEmpty()) {return;}
params.forEach(this::push);
}
/**
* 将 SeparaterRequest 地址增加到 Redis 队列中
* @param separateRequest SeparaterRequest 地址
*/
void push(SeparateRequest separateRequest);
/**
* 弹出队列
* @return SeparaterRequest 对象
*/
Request poll();
/**
* 查看 separateRequest 是否反复
* @param separateRequest 封装的爬虫 url 地址
* @return 是否反复
*/
boolean isDuplicate(SeparateRequest separateRequest);
/**
* 默认 URL 地址生成 Token<br/>
* 倡议不同的 URLParser 依照站点地址的特点,生成较短的 token
* 默认采纳 site、type、url 地址进行下划线宰割
* @param separateRequest 封装的爬虫 url 地址
*/
default String generalToken(SeparateRequest separateRequest) {return separateRequest.getSite() + "_" + separateRequest.getType() + "_" + separateRequest.getUrl();
}
/**
* 获取队列总长度
* @return 队列长度
*/
Long getQueueLength();}
4 浏览器调用爬虫(Python)
浏览器调用爬虫次要借助 Selenium 与 ChromeDriver 技术,通过本地化浏览器调用形式加载并解析页面内容,实现数据抓取。浏览器调用次要解决简单站点的数据抓取,局部站点通过流程拆分、逻辑封装、代码拆分、代码混同等形式减少代码剖析的复杂度,联合申请拆分、数据加密、客户端行为剖析等形式进行反爬操作,使爬虫程序无奈模仿客户端申请过程向服务端发动申请。
该种形式次要利用于顺丰快递单号查问过程,订单查问采纳腾讯滑动验证码插件进行人机验证。根本流程如下图所示:
首先配置 ChromeDriver 组件到操作系统中,组件下载地址:
https://chromedriver.storage.googleapis.com/index.html?path=2…,将文件保留到零碎环境变量“PATH”中指定的任意门路下,倡议:C:\Windows\system32 目录下。
组件增加验证:启用命令行窗口 -> 任意门路运行“ChromeDriver.exe”,程序会以服务模式运行。
爬虫实现过程如下:
(1)启动浏览器:为了实现爬虫的并发,须要通过参数形式对浏览器进行优化设置;
启动浏览器
def getChromeDriver(index):
options = webdriver.ChromeOptions()
options.add_argument("--incognito") # 无痕模式
options.add_argument("--disable-infobars") # 敞开菜单栏
options.add_argument("--reset-variation-state") # 重置校验状态
options.add_argument("--process-per-tab") # 独立每个 tab 页为独自过程
options.add_argument("--disable-plugins") # 禁用所有插件
options.add_argument("headless") # 暗藏窗口
proxy = getProxy()
if proxy is not None:
options.add_argument("--proxy-server==http://%s:%s" % (proxy["host"], proxy["port"])) # 增加代理
return webdriver.Chrome(chrome_options=options)
(2)加载页面:调用浏览器拜访指定地址页面,并期待页面加载实现;
driver.get('http://www.sf-express.com/cn/sc/dynamic_function/waybill/#search/bill-number/' + bill_number)
driver.implicitly_wait(20) #最长期待 20 秒加载工夫
(3)切换 Frame:验证码采纳 IFrame 形式加载到以后页面,接下来须要对页面元素进行操作,须要将 driver 切换到 iframe 中;
driver.switch_to.frame("tcaptcha_popup")
driver.implicitly_wait(10) #期待切换实现,其中 iframe 加载可能有提早
(4)滑动模块:滑动页面中的滑块到指定地位,实现验证过程;
滑动验证码操作过程如下图所示:
滑动模块执行间隔在 240 像素前后,整个滑动过程取样 14 个,模仿抛物线执行过程管制滑动速度,将这个滑动过程分为 20 次挪动(防止每次采样后果雷同),剖析如下图所示:
代码如下:
# 随机生成滑块拖动轨迹
def randomMouseTrace():
trace = MouseTrace()
trace.x = [20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 9, 9, 8, 8, 7, 7, 6, 6, 5, 5]
trace.y = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
trace.time = [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]
return trace
# 拖动滑块
def dragBar(driver, action):
dragger = driver.find_element_by_id("tcaptcha_drag_button")
action.click_and_hold(dragger).perform() # 鼠标左键按下不放
action.reset_actions()
trace = randomMouseTrace()
for index in range(trace.length()):
action.move_by_offset(trace.x[index], trace.y[index]).perform() # 挪动一个位移
action.reset_actions()
time.sleep(trace.time[index]) # 期待进展工夫
action.release().perform() # 鼠标左键松开
action.reset_actions()
return driver.find_element_by_id("tcaptcha_note").text == ""
(4)数据解析及存储:数据解析过程次要是依照 id 或 class 进行元素定位获取文本内容,将后果插入到数据库即实现数据抓取过程。
(5)其余:采纳 Python 的 threading 进行多线程调用,将订单编号保留到 Redis 中实现分布式工作获取过程,每执行一次将 POP 一个订单编号。
待改良:
(1)模块滑动速度和工夫固定,能够进行随机优化;
(2)未辨认滑块开释地位,目前采纳滑块更新的形式重试,存在肯定的错误率;
(3)如果不切换代理 IP,须要对浏览器启动进行优化,缩小启动次数,晋升抓取速度;
5 gocolly 框架(Go)
多线程并发执行是 Go 语言的劣势之一,Go 语言通过“协程”实现并发操作,当执行的协程产生 I / O 阻塞时,会由专门协程进行阻塞工作的治理,对服务器资源依赖更少,抓取效率也会有所提高。
(以下内容转自:https://www.jianshu.com/p/23d4ecb8428f)
5.1 概述
gocolly 是用 go 实现的网络爬虫框架 gocolly 疾速优雅,在单核上每秒能够发动 1K 以上申请;以回调函数的模式提供了一组接口,能够实现任意类型的爬虫;依赖 goquery 库能够像 jquery 一样抉择 web 元素。
gocolly 的官方网站是 http://go-colly.org/,提供了具体的文档和示例代码。
5.2 装置配置
装置
go get -u github.com/gocolly/colly/
引入包
import "github.com/gocolly/colly"
5.3 流程阐明
5.3.1 应用流程
应用流程次要是阐明应用 colly 抓取数据前的筹备工作
- 初始化 Collector 对象,Collector 对象是 colly 的全局句柄
- 设置全局设置,全局设置次要是设置 colly 句柄的代理设置等
- 注册抓取回调函数,次要是用于在抓取数据后在数据处理的各个流程提取数据以及登程其余操作
- 设置辅助工具,如抓取链接的寄存队列,数据荡涤队列等
- 注册抓取链接
- 启动程序开始抓取
5.3.2 抓取流程
每次抓取数据流程中的各个节点都会尝试触发用户注册的抓取回调函数,以实现提取数据等需要, 抓取流程如下。
- 依据链接每次筹备抓取数据前调用 注册的 OnRequest 做每次抓取前的预处理工作
- 当抓取数据失败时会调用 OnError 做错误处理
- 抓取到数据后调用OnResponse,做刚抓到数据时的解决工作
- 而后剖析抓取到的数据会依据页面上的 dom 节点触发 OnHTML 回调进行数据分析
- 数据分析结束后会调用 OnScraped 函数进行每次抓取后的收尾工作
5.4. 辅助接口
colly 也提供了局部辅助接口,帮助实现数据抓取剖析流程, 以下列举一部分次要的反对。
- queue 用于寄存期待抓取的链接
- proxy 用于代理发动抓取源
- thread 反对多携程并发解决
- filter 反对对非凡链接进行过滤
- depth 能够设置抓取深度管制抓取
5.5. 实例
更多能够参考源码链接中的例子(https://github.com/gocolly/colly/tree/master/_examples)