Scrapy笔记12- 抓取动态网站

前面我们介绍的都是去抓取静态的网站页面,也就是说我们打开某个链接,它的内容全部呈现出来。 但是如今的互联网大部分的web页面都是动态的,经常逛的网站例如京东、淘宝等,商品列表都是js,并有Ajax渲染, 下载某个链接得到的页面里面含有异步加载的内容,这样再使用之前的方式我们根本获取不到异步加载的这些网页内容。

使用Javascript渲染和处理网页是种非常常见的做法,如何处理一个大量使用Javascript的页面是Scrapy爬虫开发中一个常见的问题, 这篇文章将说明如何在Scrapy爬虫中使用scrapy-splash来处理页面中得Javascript。

scrapy-splash简介

scrapy-splash利用Splash将javascript和Scrapy集成起来,使得Scrapy可以抓取动态网页。

Splash是一个javascript渲染服务,是实现了HTTP API的轻量级浏览器,底层基于Twisted和QT框架,Python语言编写。所以首先你得安装Splash实例

安装docker

官网建议使用docker容器安装方式Splash。那么首先你得先安装docker

参考官方安装文档,这里我选择Ubuntu 12.04 LTS版本安装

升级内核版本,docker需要3.13内核

$ sudo apt-get update
$ sudo apt-get install linux-image-generic-lts-trusty
$ sudo reboot

安装CA认证

$ sudo apt-get install apt-transport-https ca-certificates

增加新的GPGkey

$ sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D

打开/etc/apt/sources.list.d/docker.list,如果没有就创建一个,然后删除任何已存在的内容,再增加下面一句

deb https://apt.dockerproject.org/repo ubuntu-precise main

更新APT

$ sudo apt-get update
$ sudo apt-get purge lxc-docker
$ apt-cache policy docker-engine

安装

docker engine

$ sudo apt-get install docker-engine

启动docker服务

$ sudo service docker start

验证是否启动成功

$ sudo docker run hello-world

上面这条命令会下载一个测试镜像并在容器中运行它,它会打印一个消息,然后退出。

安装Splash

拉取镜像下来

$ sudo docker pull scrapinghub/splash

启动容器

$ sudo docker run -p 5023:5023 -p 8050:8050 -p 8051:8051 scrapinghub/splash

现在可以通过0.0.0.0:8050(http),8051(https),5023 (telnet)来访问Splash了。

安装scrapy-splash

使用pip安装

$ pip install scrapy-splash

配置scrapy-splash

在你的scrapy工程的配置文件settings.py中添加

SPLASH_URL = 'http://192.168.203.92:8050'

添加Splash中间件,还是在settings.py中通过DOWNLOADER_MIDDLEWARES指定,并且修改HttpCompressionMiddleware的优先级

DOWNLOADER_MIDDLEWARES = {
'scrapy_splash.SplashCookiesMiddleware': 723,
'scrapy_splash.SplashMiddleware': 725,
'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 810,
}

默认情况下,HttpProxyMiddleware的优先级是750,要把它放在Splash中间件后面

设置Splash自己的去重过滤器

DUPEFILTER_CLASS = 'scrapy_splash.SplashAwareDupeFilter'

如果你使用Splash的Http缓存,那么还要指定一个自定义的缓存后台存储介质,scrapy-splash提供了一个scrapy.contrib.httpcache.FilesystemCacheStorage的子类

HTTPCACHE_STORAGE = 'scrapy_splash.SplashAwareFSCacheStorage'

如果你要使用其他的缓存存储,那么需要继承这个类并且将所有的scrapy.util.request.request_fingerprint调用替换成scrapy_splash.splash_request_fingerprint

使用scrapy-splash

SplashRequest

最简单的渲染请求的方式是使用scrapy_splash.SplashRequest,通常你应该选择使用这个

yield SplashRequest(url, self.parse_result,
args={
# optional; parameters passed to Splash HTTP API
'wait': 0.5, # 'url' is prefilled from request url
# 'http_method' is set to 'POST' for POST requests
# 'body' is set to request body for POST requests
},
endpoint='render.json', # optional; default is render.html
splash_url='<url>', # optional; overrides SPLASH_URL
slot_policy=scrapy_splash.SlotPolicy.PER_DOMAIN, # optional
)

另外,你还可以在普通的scrapy请求中传递splash请求meta关键字达到同样的效果

yield scrapy.Request(url, self.parse_result, meta={
'splash': {
'args': {
# set rendering arguments here
'html': 1,
'png': 1, # 'url' is prefilled from request url
# 'http_method' is set to 'POST' for POST requests
# 'body' is set to request body for POST requests
}, # optional parameters
'endpoint': 'render.json', # optional; default is render.json
'splash_url': '<url>', # optional; overrides SPLASH_URL
'slot_policy': scrapy_splash.SlotPolicy.PER_DOMAIN,
'splash_headers': {}, # optional; a dict with headers sent to Splash
'dont_process_response': True, # optional, default is False
'dont_send_headers': True, # optional, default is False
'magic_response': False, # optional, default is True
}
})

Splash API说明,使用SplashRequest是一个非常便利的工具来填充request.meta['splash']里的数据

  • meta[‘splash’][‘args’] 包含了发往Splash的参数。
  • meta[‘splash’][‘endpoint’] 指定了Splash所使用的endpoint,默认是render.html
  • meta[‘splash’][‘splash_url’] 覆盖了settings.py文件中配置的Splash URL
  • meta[‘splash’][‘splash_headers’] 运行你增加或修改发往Splash服务器的HTTP头部信息,注意这个不是修改发往远程web站点的HTTP头部
  • meta[‘splash’][‘dont_send_headers’] 如果你不想传递headers给Splash,将它设置成True
  • meta[‘splash’][‘slot_policy’] 让你自定义Splash请求的同步设置
  • meta[‘splash’][‘dont_process_response’] 当你设置成True后,SplashMiddleware不会修改默认的scrapy.Response请求。默认是会返回SplashResponse子类响应比如SplashTextResponse
  • meta[‘splash’][‘magic_response’] 默认为True,Splash会自动设置Response的一些属性,比如response.headers,response.body等

如果你想通过Splash来提交Form请求,可以使用scrapy_splash.SplashFormRequest,它跟SplashRequest使用是一样的。

Responses

对于不同的Splash请求,scrapy-splash返回不同的Response子类

  • SplashResponse 二进制响应,比如对/render.png的响应
  • SplashTextResponse 文本响应,比如对/render.html的响应
  • SplashJsonResponse JSON响应,比如对/render.json或使用Lua脚本的/execute的响应

如果你只想使用标准的Response对象,就设置meta['splash']['dont_process_response']=True

所有这些Response会把response.url设置成原始请求URL(也就是你要渲染的页面URL),而不是Splash endpoint的URL地址。实际地址通过response.real_url得到

Session的处理

Splash本身是无状态的,那么为了支持scrapy-splash的session必须编写Lua脚本,使用/execute

function main(splash)
splash:init_cookies(splash.args.cookies) -- ... your script return {
cookies = splash:get_cookies(),
-- ... other results, e.g. html
}
end

而标准的scrapy session参数可以使用SplashRequest将cookie添加到当前Splash cookiejar中

使用实例

接下来我通过一个实际的例子来演示怎样使用,我选择爬取京东网首页的异步加载内容。

京东网打开首页的时候只会将导航菜单加载出来,其他具体首页内容都是异步加载的,下面有个”猜你喜欢”这个内容也是异步加载的, 我现在就通过爬取这个”猜你喜欢”这四个字来说明下普通的Scrapy爬取和通过使用了Splash加载异步内容的区别。

首先我们写个简单的测试Spider,不使用splash:

class TestSpider(scrapy.Spider):
name = "test"
allowed_domains = ["jd.com"]
start_urls = [
"http://www.jd.com/"
] def parse(self, response):
logging.info(u'---------我这个是简单的直接获取京东网首页测试---------')
guessyou = response.xpath('//div[@id="guessyou"]/div[1]/h2/text()').extract_first()
logging.info(u"find:%s" % guessyou)
logging.info(u'---------------success----------------')

然后运行结果:

2019-04-18 14:42:44 test_spider.py[line:20] INFO ---------我这个是简单的直接获取京东网首页测试---------
2019-04-18 14:42:44 test_spider.py[line:22] INFO find:None
2019-04-18 14:42:44 test_spider.py[line:23] INFO ---------------success----------------

我找不到那个”猜你喜欢”这四个字

接下来我使用splash来爬取

import scrapy
from scrapy_splash import SplashRequest class JsSpider(scrapy.Spider):
name = "jd"
allowed_domains = ["jd.com"]
start_urls = [
"http://www.jd.com/"
] def start_requests(self):
splash_args = {
'wait': 0.5,
}
for url in self.start_urls:
yield SplashRequest(url, self.parse_result, endpoint='render.html',
args=splash_args) def parse_result(self, response):
logging.info(u'----------使用splash爬取京东网首页异步加载内容-----------')
guessyou = response.xpath('//div[@id="guessyou"]/div[1]/h2/text()').extract_first()
logging.info(u"find:%s" % guessyou)
logging.info(u'---------------success----------------')

运行结果:

2019-04-18 14:42:51 js_spider.py[line:36] INFO ----------使用splash爬取京东网首页异步加载内容-----------
2019-04-18 14:42:51 js_spider.py[line:38] INFO find:猜你喜欢
2019-04-18 14:42:51 js_spider.py[line:39] INFO ---------------success----------------

可以看出结果里面已经找到了这个”猜你喜欢”,说明异步加载内容爬取成功!

 

Scrapy笔记12- 抓取动态网站的更多相关文章

  1. 在Scrapy项目【内外】使用scrapy shell命令抓取 某网站首页的初步情况

    Windows 10家庭中文版,Python 3.6.3,Scrapy 1.5.0, 时隔一月,再次玩Scrapy项目,希望这次可以玩的更进一步. 本文展示使用在 Scrapy项目内.项目外scrap ...

  2. 手把手视频:万能开源Hawk抓取动态网站

    Hawk是沙漠之鹰历时五年开发的开源免费网页抓取工具(爬虫),无需编程,全部可视化. 自从上次发布Hawk 2.0过了小半年,可是还是有不少朋友通过邮件或者微信的方式询问如何使用.看文档还是不如视频教 ...

  3. scrapy和selenium结合抓取动态网页

    1.安装python (我用的是2.7版本的) 2.安装scrapy:   详情请参考 http://blog.csdn.net/wukaibo1986/article/details/8167590 ...

  4. 【转】详解抓取网站,模拟登陆,抓取动态网页的原理和实现(Python,C#等)

    转自:http://www.crifan.com/files/doc/docbook/web_scrape_emulate_login/release/html/web_scrape_emulate_ ...

  5. [转]使用scrapy进行大规模抓取

    原文:http://www.yakergong.net/blog/archives/500 使用scrapy有大概半年了,算是有些经验吧,在这里跟大家讨论一下使用scrapy作为爬虫进行大规模抓取可能 ...

  6. 使用scrapy-selenium, chrome-headless抓取动态网页

        在使用scrapy抓取网页时, 如果遇到使用js动态渲染的页面, 将无法提取到在浏览器中看到的内容. 针对这个问题scrapy官方给出的方案是scrapy-selenium, 这是一个把sel ...

  7. python网络爬虫抓取动态网页并将数据存入数据库MySQL

    简述以下的代码是使用python实现的网络爬虫,抓取动态网页 http://hb.qq.com/baoliao/ .此网页中的最新.精华下面的内容是由JavaScript动态生成的.审查网页元素与网页 ...

  8. scrapy-splash抓取动态数据例子八

    一.介绍 本例子用scrapy-splash抓取界面网站给定关键字抓取咨询信息. 给定关键字:个性化:融合:电视 抓取信息内如下: 1.资讯标题 2.资讯链接 3.资讯时间 4.资讯来源 二.网站信息 ...

  9. java抓取动态生成的网页

    最近在做项目的时候有一个需求:从网页面抓取数据,要求是首先抓取整个网页的html源码(后期更新要使用到).刚开始一看这个简单,然后就稀里哗啦的敲起了代码(在这之前使用过Hadoop平台的分布式爬虫框架 ...

随机推荐

  1. 别傻傻不知道 == 和 equals 的区别【面试系列】

    ​ 关于这个问题,一般初中级面试中都会遇到,还记得我当初实习找工作的时候也遇到了这个问题,现在都还记得自己是怎么回答的:== 是基本类型比较,equals 是对象比较,不懂 hashCode,想起来简 ...

  2. SQLAIchemy(二)ORM 相关

    0. 前言 对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术 简单的说,ORM是通过使用描述对象和数据库之 ...

  3. SpringBoot 第一篇:入门篇

    作者:追梦1819 原文:https://www.cnblogs.com/yanfei1819/p/10819728.html 版权声明:本文为博主原创文章,转载请附上博文链接! 前言   博主从去年 ...

  4. 部门工资前三高的所有员工 - LeetCode

    Employee 表包含所有员工信息,每个员工有其对应的工号 Id,姓名 Name,工资 Salary 和部门编号 DepartmentId . +----+-------+--------+---- ...

  5. ForkJoin和流式操作

    Fork/Join框架:在必要的情况下,将一个大任务,进行拆分(fork) 成若干个子任务(拆到不能再拆,这里就是指我们制定的拆分的临界值),再将一个个小任务的结果进行join汇总. 采用juc包的f ...

  6. Linux ip Command

    Syntax ip OBJECT COMMAND ip [options] OBJECT COMMAND ip OBJECT help Understanding ip command OBJECTS ...

  7. 黑科技!仅需 3 行代码,就能将 Gitter 集成到个人网站中,实现一个 IM 即时通讯聊天室功能?

    欢迎关注个人微信公众号: 小哈学Java, 文末分享阿里 P8 高级架构师吐血总结的 <Java 核心知识整理&面试.pdf>资源链接!! 个人网站: https://www.ex ...

  8. 02 .NET CORE 2.2 使用OCELOT -- 路由

    继续学习.NET CORE 2.2 使用OCELOT https://www.jianshu.com/p/05ccf87a3091 https://www.jianshu.com/p/585396dc ...

  9. ALBERT+BiLSTM+CRF实现序列标注

    一.模型框架图 二.分层介绍 1)ALBERT层 albert是以单个汉字作为输入的(本次配置最大为128个,短句做padding),两边分别加上开始标识CLS和结束标识SEP,输出的是每个输入wor ...

  10. 开发技术-IPython介绍

    开发技术-IPython介绍 IPython是交互式的Python命令行,希望大家喜欢. 最近在回顾之前学习的内容,并且继续学习新的知识.这种自身的感觉,很不一般,有太多的感想产生.希望自己可以坚持将 ...