作者:京东物流 田禹

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@NoArgsConstructorpublic 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@Datapublic 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)