scrapy爬取动态网页_基于scrapy的动态网页采集方案总结

2023-11-08

基于scrapy的动态网页采集一直是个难点,而且如果想达到工程级别的抓取,需要有个高效率的解决方案。我列出了几个曾经尝试过的方案和它们的特点。

基于PyV8等脚本解析引擎

这类方案的原理是利用开源浏览器项目中的脚本解释引擎,实现相关脚本片段的解析,从而获得动态页面主体内容与超连接网络地址。自行构建脚本环境的方法以JavaScript脚本解析引擎为基础,构造解析动态页面脚本程序片段的环境。由于JavaScript脚本解析引擎只对JavaScript语言的基本对象做了支持,无法解析动态页面嵌入的JavaScript片段所使用的浏览器DOM对象,因而在利用这些脚本解析引擎解析动态页面脚本片段时,需要自己实现DOM对象,使脚本解析引擎支持浏览器DOM对象。

网页中Javascript片段示例

这种方法能够在非web环境下执行独立的Javascript语句,可以细粒度地控制网页中Javascript的执行。但由于实际抓取时缺少web环境,无法对如上图所示的DOM树等对象进行操作,因此这种方案很难适用。

自动化测试工具 + 浏览器

这类方案使用开源浏览器渲染整个动态页面,从浏览器输出结果中提取页面主体内容与超链接网络地址。Selenium是由thought Works公司开发的web自动化测试工具。它直接在浏览器中运行,就像真正的用户在操作一样。Selenium框架底层使用Javascript模拟真实用户对浏览器进行操作。当执测试脚本时,浏览器自动按照做出相应的操作,就仿佛真实用户一样,因而可以从终端用户的角度测试Web应用。下图是在并发数为4,抓取20条URL情况下各浏览器性能对比:

并发数4,抓取20条URL各浏览器性能对比

这种方法能够完整得到浏览器渲染过的动态网页源码,但它需要调用外部程序,CPU和内存资源占用较高。由上图可以发现各个浏览器的用时、CPU占用和内存占用对实际应用来说是无法接受的;同时,该方案执行了很多和页面的文本内容和链接无关的脚本片段,针对性不够强,会使得整个系统的性能消耗过大,导致其效率低下,严重影响数据的采集速度。

基于QtWebkit的下载中间件

WebKit 是一个开源的浏览器引擎,相比于其他浏览器引擎来说,Webkit的优势在于高效稳定,兼容性好,且源码结构清晰,易于维护。而QtWebkit是集成了Qt接口的Webkit实现,底层由C++开发,本方案中使用的是该内核的Python封装PyQt。在具体实现中,我们单独构建一个独立的下载中间件,在process_response函数中对网页源码进行动态渲染,并将渲染后得到的HTML代码替换源码。

它能够利用其中的脚本解析引擎执行页面中嵌入的JavaScript脚本程序片段;无需调用外部程序,系统资源占用较小。但这种方案很难适用,因为QtWebkit的调用方法与下载中间件的结构设计无法统一,导致爬虫退出时QtWebkit异常崩溃;下载中间件渲染网页时,其他请求会被阻塞,渲染效率较低。

基于ghost.py的下载中间件

ghost.py是对QtWebkit进一步封装,封装后的调用方法与下载中间件的结构能够较好地结合,解决了爬虫退出时的崩溃问题;同时,ghost.py能够创建DOM对象,构造解析动态页面脚本程序片段的环境,支持单条Javascript语句的执行;在具体实现中,我们仍然构建一个独立的下载中间件,处理返回结果时进行动态渲染,并将渲染后得到的HTML代码替换源码。

这种方法可以细粒度地控制渲染细节,比如禁用图片设置、资源加载超时设置等。但是,下载中间件是处于核心引擎和下载器之间,爬虫运行时只有一个示例对象,这样的系统定位导致网页渲染仍然是阻塞式的,执行效率较低。

基于ghost.py的下载器

改造scrapy的下载器,在下载器中使用ghost.py完成网页源码解压和动态网页渲染的步骤,这种方案的目的是尝试在下载器多线程并发下载的同时能够并发渲染,以期能够提高下载器的抓取效率。关于下载中间件渲染和下载器渲染的系统结构区别如下图

下载中间件与下载器示意图

我们的测试结果表明这种方案下爬虫的CPU使用率较低,下载器的线程无法利用多核执行;网络I/O和动态解析等待耗时长,数据抓取效率难以提升。这是因为:Python语言具有全局解释器锁的特性,它的作用在于让同一时刻只能有一个线程对于Python对象进行操作,这使得逻辑上并发的线程物理上仍然是顺序执行的;网页的渲染缺少异步处理的能力,单个线程会被挂起等待,直到渲染结果返回才会唤醒继续执行,这也导致数据抓取效率的降低。

在我们使用ghost.py的过程,我们遇到一个非常棘手的内存段错误的bug,爬虫每次在爬取到几百条URL之后会出现core dump异常并自动结束程序。我们使用dmesg命令检测内核环缓冲,了解系统的启动信息,得到如下的错误信息提示:

内存段错误提示

我们计算得到libQtWebkit.so.4.8.1中出错函数的相对地址: A1E7F9。然后利用gdb调试反汇编代码,定位到运行出错的机器码如下图:

定位至出错的机器码

经过初步分析,我们认为出错发生在程序访问eax地址时,内存读操作越界。该错误出现在QtWebkit中CSS色彩梯度渐变渲染函数内,这应该是PyQt 4.8.1版本中的一个小bug,而修改源代码涉及的工作较为复杂且工程浩大,故也因此舍弃了该方案。

基于splash渲染服务

通过对前面几种技术的分析和测试,我们发现这些方案都存在服务器资源利用效率不高、运行效率低和不稳定性等问题,于是我们考虑引入一种基于splash服务的动态解析方案。splash是基于Twisted异步处理框架和Qt开发框架实现的、对外提供HTTP API的一种网页渲染服务,其中Twisted使渲染服务具有异步处理能力,可以发挥Webkit的并发能力。结合了splash的scrapy系统架构和工作方式如下图:

scrapy+splash示意图

splash的动态渲染服务是我们最终采用的方案,但它在应用时有如下问题:splash的启停和参数设置无法由scrapy控制;splash运行时有极小概率崩溃;splash的endpoint参数设置对抓取效率和内存占用影响较大。

虽然splash内部采用了Twisted异步处理框架,但这种方法只能节省线程等待网络IO和CPU渲染的时间,splash仍然会受困于Python全局解释器锁的限制,无法有效地利用多个CPU内核进行处理。单个splash即使满负荷运行也只能占用一个内核。而且原有方案是一个scrapy进程关联一个splash服务,splash的启停由scrapy单独控制,不与别的scrapy进程共享。这种即时开关splash服务的方法本省会消耗系统宝贵的计算资源,也会给整个爬虫系统的运行带来不稳定性。同时,不同splash服务之间的负载不均衡,也会给系统整体的抓取性能带来损害。

所以,我们利用nginx反向代理服务来实现splash服务的负载均衡。nginx是一款轻量级的、高性能的Web 服务器/反向代理服务器,这里我们将它用作反向代理服务器来实现负载均衡。反向代理方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。

nginx可以很容易通过模块扩展,所以nginx所拥有的功能集合也是很大的。nginx中的upstream负载均衡支持下面几种方式:轮询(默认):按照时间顺序对所有服务器一个一个的访问,如果有服务器宕机,会自动剔除;权重:服务器的访问概率和权重成正比,这个可以在服务器配置不均的时候进行配置;IP哈希:对每个请求的IP进行哈希计算,并按照一定的规则分配对应的服务器;公平:按照每台服务器的响应时间来分配请求,响应时间小的优先分配;URL哈希:按照访问URL的哈希值来分配请求。

下面的测试中,我们使用单个scrapy进程进行抓取,一共抓取991条URL,通过nginx反向代理请求不同数量的splash渲染服务,分别在scrapy请求并发数为50、100和150三种情况下进行对比。其中nginx在实现负载均衡时采用了默认的轮训方式。测试结果如下图:

不同splash数量的用时对比

从图中可以发现,在相同的scrapy请求并发数情况下,利用更多的splash渲染服务能够明显地获得更高的抓取效率;但如果在拥有同样数量的splash服务时,单纯地去提高scrapy请求并发数,得到的抓取效率提升就显得相对有限,这很可能是因为整体的性能瓶颈此时已经不再是渲染需要的CPU利用率了,而是变成scrapy自身处理效率及整体网络通信的瓶颈。

splash的启动

splash既可以在程序里使用依赖docker.py进行启动,或者在命令行中人工启动。通过程序启动的示例如下:

con = client.create_container(image="scrapinghub/splash", volumes=["/etc/splash/filters"], ports=[8050], host_config=client.create_host_config(port_bindings={8050:port_use}, binds=["/home/spider/Downloads:/etc/splash/filters"]))

其中参数image是该容器启动的映像名字;volume代表容器内部使用的挂载,其中的地址与外部文件无关,是定义在容器内一块文件;ports代表该容器会监听自己内部的8050端口,这是splash内部固定设置,不建议更改;host_config是建立主机和容器的一种绑定关系的设置,绑定包括两部分内容,一个是内部的8050端口和外部port_use端口的绑定,如果以后确定scrapy请求的端口号设置为8060,则将port_use设置为8060,另外一个是外部文件和容器内部挂载的绑定,冒号后面的目录是之前volumes的值,冒号之前的目录则填写你自定义过滤文件的目录,默认取名为default.txt,例如在后面会提到的请求过滤规则文件default.txt我们放在”/home/spider/Downloads”目录下,则像上述代码一样填写即可。

splash还有另外一种命令行人工启动的方法,需要敲入的命令是:

[root@slave1 ~]# docker run -p 8053:8050 –v /home/spider/Downloads:/etc/splash/filters scrapinghub/splash

这里的-p后跟的8053代表主机提供scrapy的请求端口,8050仍然是docker容器内部定义的固定端口;-v仍然是对volumes的配置,冒号前为本机实际的文件目录,冒号后是容器内部的文件系统,不用更改;最后添加的scrapinghub/splash是容器要启动的映像名称。

splash的使用

  • 在scrapy的setting.py文件中添加:
SPLASH_URL = 'http://192.168.0.1:8053'
  • 在scrapy的setting.py文件中修改下载中间件的设置:
DOWNLOADER_MIDDLEWARES = {
    'scrapy_splash.SplashCookiesMiddleware': 723,
    'scrapy_splash.SplashMiddleware': 725,
'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 810,
}
  • 在scrapy的setting.py文件中添加去重中间件:
SPIDER_MIDDLEWARES = {
    'scrapy_splash.SplashDeduplicateArgsMiddleware': 100,
}
  • 设置定制的去重类:
DUPEFILTER_CLASS = 'scrapy_splash.SplashAwareDupeFilter'
  • 设置URL使用splash渲染服务:
yield scrapy.Request(url, self.parse_result, meta={
    		'splash': {
        		'args': {
            		# set rendering arguments here
            		'html': 1,
        		},
        	'endpoint': 'render.json',  # optional; default is render.json
        	'splash_url': '<url>',      # optional; overrides SPLASH_URL
    		}
})

splash请求过滤文件

! customize
.css|
.jpg|
.png|
.gif|
	
! Fork of: ChinaList https://github.com/chinalist/chinalist
/adflash/*
/attachments/ad/*
/tan1.js
@@||poster.weather.com.cn/p_files/base/$image
@@||union.bokecc.com/crossdomain.xml
hk.search.auctions.yahoo.com###yauadysmh
hk.yahoo.com###mntl1
||114lm.com^$third-party
||116b.com^$third-party
……

上述代码列举了CSS文件,图片文件和部分广告链接等。将此文件改名为default.txt即可。

nginx负载均衡的配置和使用

  • 安装nginx,只需要命令行使用yum install即可:
[root@slave1 ~]# yum install nginx
  • 运行前修改配置文件/etc/nginx/nginx.conf,在http处添加:
http { 
  	  upstream splash{
        	 server 223.3.95.199:8051;
        	 server 223.3.95.199:8052;
        	 server 223.3.95.199:8053;
       	  }
}
  • 再修改配置文件/etc/nginx/conf.d/default.conf:
server {
 	   listen         8050;
 	   server_name    mySplash;
	
    	location / {
        	proxy_pass http://splash;
   	 }
}
  • 开启nginx进程:
[root@slave1 spider]# /usr/sbin/nginx
  • 重启或关闭进程:
[root@slave1 spider]# /usr/sbin/nginx –s reload
[root@slave1 spider]# /usr/sbin/nginx –s stop
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

scrapy爬取动态网页_基于scrapy的动态网页采集方案总结 的相关文章

  • 【笔记】对称密码之分组密码的工作模式

    目录 一 前言 二 分组密码概述 2 1分组密码的设计原则 2 2混淆和扩散 三 分组加密法的模式 3 1电码本ECB Electronic Code Book 模式 3 2密码分组链接CBC Cipher Block Chaining 模
  • HTTP3

    当我对HTTP的认知还停留在HTTP2 0时 HTTP协议已经发展3 0了 参考下知乎 HTTP 3 原理实战 知乎 大厂对于新技术的追求总是处于行业前列 HTTP3就是其中之一 既然大厂都逐渐在使用了 那说明它经过了一系列的实践的考验 具
  • 二分搜索——分治思想

    二分查找 二分查找是一种在每次比较之后将查找空间一分为二的算法 每次需要查找集合中的索引或元素时 都应该考虑二分查找 如果集合是无序的 我们可以总是在应用二分查找之前先对其进行排序 时间复杂度是 log N 因为 二分查找是通过将现有数组一
  • 数据结构C语言 单链表(插入、删除、查找)

    数据结构C语言 单链表 插入 删除 查找 1 插入 假设 A 的临时指针为 p C 的临时指针为 q 步骤1 删除这条连接线 步骤2 将p gt next给q gt next 步骤3 将q给p gt next 插入代码 q gt next
  • ubuntu18.04+cuda10.2+cudnn7.6.5,并使用CUDA自动安装NVIDIA驱动而非手动。

    一 CUDA和NVIDIA显卡驱动安装 cuda的安装选项中其实包含了nvidia驱动的安装选项 不过网上好多资料都说不要再cuda中勾选nvidia驱动 而要自己去nvidia官网自己查好型号下载安装文件 手动安装nvidia驱动 其实主
  • 字体格式:ttf,woff,eot

    生成网页字体 https onlinefontconverter com eot IE onetype是微软和Adobe共同开发的字体 IE浏览器全部采用这种字体 woff 其它浏览器 woff web开发字体格式 是一种专门为web而设计
  • 信号延迟仿真的 Matlab 源码实现

    信号延迟仿真的 Matlab 源码实现 信号的延迟是数字信号处理中的一个重要概念 本文将介绍如何使用 Matlab 实现信号的延迟仿真 并给出相应的源代码实现 首先 我们需要定义一个信号并进行时域分析 在 Matlab 中 我们可以使用 t
  • Ubuntu下卸载Qt

    卸载有2种办法 1 进入qt的安装目录下卸载 一般ubuntu软件是安装在opt目录下 如果不在就需要找找了 进入安装目录下 sudo MaintenanceTool 选择remove all 就可以完全删除qt了 2 命令行安装的卸载 s
  • OVS datapath流表结构及匹配过程

    datapath流表的查找函数是ovs flow tbl lookup stats 在此之前 先看下datapath组织流表的方式 最新2 6的ovs流表 已经不是最早单纯的精确匹配了 而是一种精确匹配 带掩码匹配合并在一起的方式 叫做me
  • halcon像素统计_Halcon(八)亚像素轮廓XLD

    fast threshold Image Region 0 120 7 boundary Region RegionBorder inner dilation circle RegionClipped RegionDilation 2 5
  • Cox-Box变换

    在 回归分析的基本假设 中提到了回归分析中的基本假设 这里的Box Cox变换方法能够解决回归模型中的误差项不服从高斯分布的违例问题 通常这种违例情况出现在 误差 epsilon与预测变量相关的时候 会影响模型结果的精确度 简单的方法就是通
  • 了解redis的单线程模型工作原理?一篇文章就够了

    1 首先redis是单线程的 为什么redis会是单线程的呢 从redis的性能上进行考虑 单线程避免了上下文频繁切换问题 效率高 从redis的内部结构设计原理进行考虑 redis是基于Reactor模式开发了自己的网络事件处理器 这个处
  • 输入一个十进制数,输出其二进制,八进制,十六进制

    a int input 请输入一个十进制整数 print 其对应二进制为 b n八进制为 o n十六进制为 x format a a a
  • Java并发编程面试题——JUC专题

    一 AQS高频问题 1 1 AQS是什么 AQS是JUC下大量工具的基础类 很多工具都基于AQS实现的 比如lock锁 CountDownLatch Semaphore 线程池等等都用到了AQS AQS中有一个核心属性state 还有一个双
  • 基于react+and Design实现下拉框,支持自由输入

    基于react and Design实现下拉框 支持自由输入 以下是基于select的改造方案 使用AutoComplete组件更简单方便一些 AutoComplete这组件的实现方式请移步 基于react and Design实现下拉框
  • mysql 数据库授权(给某个用户授权某个数据库)

    mysql 数据库授权 给某个用户授权某个数据库 版权 1 小唐唐 https blog csdn net qq 38390092 article details 90340804 2 季枫 https www cnblogs com ji
  • PCB设计中常用的尺寸标注

    PCB设计中常用的尺寸标注 原创 凡亿教育 凡亿PCB 凡亿PCB 为了使设计者或生产者更方便地知晓PCB尺寸及相关信息 在设计的时候通常考虑到给设计好的PCB添加尺寸标注 尺寸标注方式分为线性 圆弧半径 角度等形式 下面对最常用的线性标注
  • canteen php,PHP脚本任务优化思路或改进方案?

    脚本部分 date default timezone set PRC require once canteen MySQL php mysql MySQL getInstance localhost root 123456 canteens
  • Oracle 修改字段非空属性问题

    背景 最近因为项目要做国际推广 然后在国外使用环境中有一个我们国内系统必填的字段是不需要的 导致一些问题所以需要修改数据库中对应字段的非空属性为允许为空 因为sql水平实在渣渣 只能网上搜索结果 找到一堆答案 但是没一个能成功执行的 不知是

随机推荐

  • ViewModelScope 避免内存泄漏的原理

    避免的是什么 避免的是协程的内存泄漏 如何避免 总体逻辑 通过 lifecycle 监听 Activity 的生命周期 在 Activity 销毁时对协程进行 cancel 监听状态变化 下图代码是注册监听的地方 可以看到在 Activit
  • 2014第五届蓝桥杯JavaB组决赛(国赛)试题汇总及试题详解

    蓝桥杯历年省赛真题汇总及题目详解 蓝桥杯历年决赛试题汇总及试题详解 目录 第一题 国王的遗产 第二题 六角幻方 第三题 格子放鸡蛋 第四题 排列序数 第五题 幂一矩阵 第六题 供水设施 第一题 国王的遗产 题目描述 X国是个小国 国王K有6
  • oracle数据库级别优化分析工具介绍

    author skatetime 2010 03 04 oracle数据库级别优化分析工具介绍 当我们对数据库优化诊断时 需要收集相应的信息以供参考 从个人的使用经验来说 这种统计数据分为两大类 一类是数据库级别的统计信息二类是os级别的统
  • 常见iPhone恢复固件(DFU模式)的三种方法

    可能你听说iPhone的DFU模式 DFU的全称是Development Firmware Upgrade 实际意思就是iPhone固件的强制升降级模式 例如 在你降级iPhone固件的时候 如果出现过错误 1 或者错误 6 那么在你恢复或
  • 如何使用ESP32相机模组实现视频流和人脸识别

    人证识别系统有许多种方式 比如使用签名 指纹 语音 面部识别等来识别人员 但是只有人脸识别系统可以检测和识别机场 零售店和火车站等公共场所中的人员 人脸识别系统不仅可以用于安全目的以识别公共场所中的人员 还可以用于办公室和学校中的考勤记录
  • ESP8266使用详解(AT,LUA,SDK)

    https www cnblogs com yangfengwu p 10100152 html 8266综合开发教程 AT LUA SDK 推荐 https www cnblogs com yangfengwu category 1187
  • android 自定义时钟控件

    效果截图 自定义时钟组件源代码 package com sky dreaming analogic clock view import android content Context import android content Inten
  • Python 中的 sequence 类型

    在查看Python 内置的帮助文档的时候 我发现其对函数的定义def是如下形式的 duplicated subset Hashable Sequence Hashable None None keep Literal first Liter
  • 基于栈的算术表达式求值算法

    基于栈的算术表达式求值算法 在计算机科学中 算术表达式求值是一个非常重要且常见的问题 通常 我们使用中缀表示法来编写算术表达式 而计算机则使用后缀表示法 也称为逆波兰表示法 来进行求值 本文将介绍一种基于栈的算法来解决这个问题 栈数据结构
  • 一、认识Luci的整体结构

    Luci采用的是MVC的Web框架 即Model View Controller usr lib lua luci controller 控制层 usr lib lua luci view 视图层 usr lib lua luci mode
  • 巴特沃斯滤波器原理及其仿真设计

    前面的几篇文章对一阶低通滤波器原理及其数字化进行了探究 为了进一步探究滤波这条知识线路 今天对巴特沃斯滤波器滤波器进行了研究 1 什么是巴特沃斯滤波器 巴特沃斯 Butterworth 滤波器是一种具有最大平坦幅度响应低通滤波器 它在通信领
  • Windows环境搭建USRP-B210开发环境

    背景 近期在搞软件无线电 SDR 买了一块USRP B210作为发射机 决定在Windows平台下开发 找遍网络 基本上都是Linux下的开发资料 连官网上都没有什么关于Windows下的开发手册 我还就不信了 MATLAB上能跑 还不能在
  • QT中各类型数据转换(更新中)

    QT类型转换 数据转换 16进制 to int型 int型 to 16进制 16进制 to float型 QString型 to 16进制 16进制 to QString型 数据转换 开发过程中通常需要数据类型的转换 最近使用QT开发工具
  • 冒泡排序实现(c++)

    目录 冒泡排序简介 冒泡排序原理 动图演示 代码实现 冒泡排序简介 冒泡排序 最优时间复杂度O N 平均时间复杂度O N 2 最差空间复杂度O N 平均时间复杂度O 1 是一种代码简单的排序也是几乎最慢的算法 稳定 冒泡排序原理 比较相邻的
  • Notepad++ 删除空白行的方法

    方法一 插件处理 先下载安装插件 TextFX 下载后重新启动下 然后在菜单栏找到 TextFX gt TextFX Edit gt Delete blank lines 即可 方法二 正则处理 选择替换 把查找模式设置为正则表达式 在查找
  • windows server 2008 r2安装SQL SERVER 2008 R2 不能打开1433端口设置方法

    服务器 WINDOWS SERVER 2008 R2 SQL SQL SERVER 2008 R2 背景 同一个公司同一个局域网 网络可以ping通 但是不能连接服务器数据库 提示错误1326 前期设置 经过前期设置都不行 telnet l
  • 提升布局灵活性:掌握Vue中vue-splitpane分割面板的实用技巧

    项目中遇到内容分割化并且可以让用户自行调整面板大小的需求 即可使用此组件解决 首先看效果 使用 npm install vue splitpane S 引入组件库 import splitPane from vue splitpane 全局
  • Altium Designer中批量修改原理图中的器件属性

    网上关于批量修改也有很多的介绍 按照网上的尝试在PCB的修改中可以正常操作 但是在原理图中 却只能修改一个 究其原因 原来是差了一步 正确的步骤是 1 先选择需要修改的器件的其中一个 2 右键find similar objects 然后在
  • 史上最全的Unity面试题(含答案)

    一 什么是渲染管道 是指在显示器上为了显示出图像而经过的一系列必要操作 渲染管道中的很多步骤 都要将几何物体从一个坐标系中变换到另一个坐标系中去 主要步骤有 本地坐标 gt 视图坐标 gt 背面裁剪 gt 光照 gt 裁剪 gt 投影 gt
  • scrapy爬取动态网页_基于scrapy的动态网页采集方案总结

    基于scrapy的动态网页采集一直是个难点 而且如果想达到工程级别的抓取 需要有个高效率的解决方案 我列出了几个曾经尝试过的方案和它们的特点 基于PyV8等脚本解析引擎 这类方案的原理是利用开源浏览器项目中的脚本解释引擎 实现相关脚本片段的