scrapy与selenium结合爬取数据(爬取动态网站)的示例代码


Posted in Python onSeptember 28, 2020

scrapy框架只能爬取静态网站。如需爬取动态网站,需要结合着selenium进行js的渲染,才能获取到动态加载的数据。

如何通过selenium请求url,而不再通过下载器Downloader去请求这个url?

方法:在request对象通过中间件的时候,在中间件内部开始使用selenium去请求url,并且会得到url对应的源码,然后再将   源 代码通过response对象返回,直接交给process_response()进行处理,再交给引擎。过程中相当于后续中间件的process_request()以及Downloader都跳过了。

相关的配置:

1、scrapy环境中安装selenium:pip install selenium

scrapy与selenium结合爬取数据(爬取动态网站)的示例代码

2、确保python环境中有phantomJS(无头浏览器)

scrapy与selenium结合爬取数据(爬取动态网站)的示例代码

对于selenium的主要操作是下载中间件部分如下图:

scrapy与selenium结合爬取数据(爬取动态网站)的示例代码

scrapy与selenium结合爬取数据(爬取动态网站)的示例代码

代码如下

middlewares.py代码:

注意:自定义下载中间件,采用selenium的方式!!

# -*- coding: utf-8 -*-

# Define here the models for your spider middleware
#
# See documentation in:
# https://doc.scrapy.org/en/latest/topics/spider-middleware.html

from scrapy import signals
from selenium import webdriver
from selenium.webdriver import FirefoxOptions
from scrapy.http import HtmlResponse, Response
import time

class TaobaospiderSpiderMiddleware(object):
 # Not all methods need to be defined. If a method is not defined,
 # scrapy acts as if the spider middleware does not modify the
 # passed objects.

 @classmethod
 def from_crawler(cls, crawler):
  # This method is used by Scrapy to create your spiders.
  s = cls()
  crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
  return s

 def process_spider_input(self, response, spider):
  # Called for each response that goes through the spider
  # middleware and into the spider.

  # Should return None or raise an exception.
  return None

 def process_spider_output(self, response, result, spider):
  # Called with the results returned from the Spider, after
  # it has processed the response.

  # Must return an iterable of Request, dict or Item objects.
  for i in result:
   yield i

 def process_spider_exception(self, response, exception, spider):
  # Called when a spider or process_spider_input() method
  # (from other spider middleware) raises an exception.

  # Should return either None or an iterable of Response, dict
  # or Item objects.
  pass

 def process_start_requests(self, start_requests, spider):
  # Called with the start requests of the spider, and works
  # similarly to the process_spider_output() method, except
  # that it doesn't have a response associated.

  # Must return only requests (not items).
  for r in start_requests:
   yield r

 def spider_opened(self, spider):
  spider.logger.info('Spider opened: %s' % spider.name)


class TaobaospiderDownloaderMiddleware(object):
 # Not all methods need to be defined. If a method is not defined,
 # scrapy acts as if the downloader middleware does not modify the
 # passed objects.

 @classmethod
 def from_crawler(cls, crawler):
  # This method is used by Scrapy to create your spiders.
  s = cls()
  crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
  return s

 def process_request(self, request, spider):
  # Called for each request that goes through the downloader
  # middleware.

  # Must either:
  # - return None: continue processing this request
  # - or return a Response object
  # - or return a Request object
  # - or raise IgnoreRequest: process_exception() methods of
  # installed downloader middleware will be called
  return None

 def process_response(self, request, response, spider):
  # Called with the response returned from the downloader.

  # Must either;
  # - return a Response object
  # - return a Request object
  # - or raise IgnoreRequest
  return response

 def process_exception(self, request, exception, spider):
  # Called when a download handler or a process_request()
  # (from other downloader middleware) raises an exception.

  # Must either:
  # - return None: continue processing this exception
  # - return a Response object: stops process_exception() chain
  # - return a Request object: stops process_exception() chain
  pass

 def spider_opened(self, spider):
  spider.logger.info('Spider opened: %s' % spider.name)

*********************下面是相应是自定义的下载中间件的替换代码**************************
class SeleniumTaobaoDownloaderMiddleware(object):
 # 将driver创建在中间件的初始化方法中,适合项目中只有一个爬虫。
 # 爬虫项目中有多个爬虫文件的话,将driver对象的创建放在每一个爬虫文件中。
 # def __init__(self):
 #  # 在scrapy中创建driver对象,尽可能少的创建该对象。
 #  # 1. 在初始化方法中创建driver对象;
 #  # 2. 在open_spider中创建deriver对象;
 #  # 3. 不要将driver对象的创建放在process_request();
 #  option = FirefoxOptions()
 #  option.headless = True
 #  self.driver = webdriver.Firefox(options=option)

 # 参数spider就是TaobaoSpider()类的对象
 def process_request(self, request, spider):
  if spider.name == "taobao":
   spider.driver.get(request.url)
   # 由于淘宝的页面数据加载需要进行滚动,但并不是所有js动态数据都需要滚动。
   for x in range(1, 11, 2):
    height = float(x) / 10
    js = "document.documentElement.scrollTop = document.documentElement.scrollHeight * %f" % height
    spider.driver.execute_script(js)
    time.sleep(0.2)

   origin_code = spider.driver.page_source
   # 将源代码构造成为一个Response对象,并返回。
   res = HtmlResponse(url=request.url, encoding='utf8', body=origin_code, request=request)
   # res = Response(url=request.url, body=bytes(origin_code), request=request)
   return res
  if spider.name == 'bole':
   request.cookies = {}
   request.headers.setDefault('User-Agent','')
  return None

 def process_response(self, request, response, spider):
  print(response.url, response.status)
  return response

taobao.py 代码如下:

# -*- coding: utf-8 -*-
import scrapy
from selenium import webdriver
from selenium.webdriver import FirefoxOptions


class TaobaoSpider(scrapy.Spider):
 """
 scrapy框架只能爬取静态网站。如需爬取动态网站,需要结合着selenium进行js的渲染,才能获取到动态加载的数据。

 如何通过selenium请求url,而不再通过下载器Downloader去请求这个url?
 方法:在request对象通过中间件的时候,在中间件内部开始使用selenium去请求url,并且会得到url对应的源码,然后再将源代码通过response对象返回,直接交给process_response()进行处理,再交给引擎。过程中相当于后续中间件的process_request()以及Downloader都跳过了。

 """
 name = 'taobao'
 allowed_domains = ['taobao.com']
 start_urls = ['https://s.taobao.com/search?q=%E7%AC%94%E8%AE%B0%E6%9C%AC%E7%94%B5%E8%84%91&imgfile=&commend=all&ssid=s5-e&search_type=item&sourceId=tb.index&spm=a21bo.2017.201856-taobao-item.1&ie=utf8&initiative_id=tbindexz_20170306']
 
 def __init__(self):
  # 在初始化淘宝对象时,创建driver
  super(TaobaoSpider, self).__init__(name='taobao')
  option = FirefoxOptions()
  option.headless = True
  self.driver = webdriver.Firefox(options=option)

 def parse(self, response):
  """
  提取列表页的商品标题和价格
  :param response:
  :return:
  """
  info_divs = response.xpath('//div[@class="info-cont"]')
  print(len(info_divs))
  for div in info_divs:
   title = div.xpath('.//a[@class="product-title"]/@title').extract_first('')
   price = div.xpath('.//span[contains(@class, "g_price")]/strong/text()').extract_first('')
   print(title, price)

settings.py代码如下图:

scrapy与selenium结合爬取数据(爬取动态网站)的示例代码

关于代码中提到的初始化driver的位置有以下两种情况:

1、只存在一个爬虫文件的话,driver初始化函数可以定义在middlewares.py的自定义中间件中(如上述代码注释初始化部分)也可以在爬虫文件中自定义(如上述代码在爬虫文件中初始化)。

注意:如果只有一个爬虫文件就不需要在自定义的process_requsests中判断是哪一个爬虫项目然后分别请求!

2、如果存在两个或两个以上爬虫项目(如下图项目结构)的时候,需要将driver的初始化函数定义在各自的爬虫项目文件下(如上述代码),同时需要在process_requsests判断是那个爬虫项目的请求!!

scrapy与selenium结合爬取数据(爬取动态网站)的示例代码          

scrapy与selenium结合爬取数据(爬取动态网站)的示例代码

到此这篇关于scrapy与selenium结合爬取数据的示例代码的文章就介绍到这了,更多相关scrapy selenium爬取数据内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
python使用装饰器和线程限制函数执行时间的方法
Apr 18 Python
python下MySQLdb用法实例分析
Jun 08 Python
Python的Django REST框架中的序列化及请求和返回
Apr 11 Python
谈谈如何手动释放Python的内存
Dec 17 Python
Django框架创建项目的方法入门教程
Nov 04 Python
Python解析多帧dicom数据详解
Jan 13 Python
Windows上安装tensorflow  详细教程(图文详解)
Feb 04 Python
Pygame框架实现飞机大战
Aug 07 Python
python操作链表的示例代码
Sep 27 Python
PyQt实现计数器的方法示例
Jan 18 Python
Python基础之元编程知识总结
May 23 Python
python创建字典及相关管理操作
Apr 13 Python
scrapy结合selenium解析动态页面的实现
Sep 28 #Python
互斥锁解决 Python 中多线程共享全局变量的问题(推荐)
Sep 28 #Python
python 常见的反爬虫策略
Sep 27 #Python
python 5个实用的技巧
Sep 27 #Python
Python日志器使用方法及原理解析
Sep 27 #Python
python 爬取免费简历模板网站的示例
Sep 27 #Python
python如何提升爬虫效率
Sep 27 #Python
You might like
php mysql Errcode: 28 终极解决方法
2009/07/01 PHP
PHP 裁剪图片成固定大小代码方法
2009/09/09 PHP
PHP array_push 数组函数
2009/12/26 PHP
php防盗链的常用方法小结
2010/07/02 PHP
如何让thinkphp在模型中自动完成session赋值小教程
2014/09/05 PHP
php数字运算验证码的实现代码
2015/07/30 PHP
[JS]点出统计器
2020/10/11 Javascript
使用jquery解析XML的方法
2014/09/05 Javascript
让IE8浏览器支持function.bind()方法
2014/10/16 Javascript
jquery处理json对象
2014/11/03 Javascript
jquery中EasyUI使用技巧小结
2015/02/10 Javascript
JS判断iframe是否加载完成的方法
2016/08/03 Javascript
AngularJs 指令详解及示例代码
2016/09/01 Javascript
jquery easyui DataGrid简单示例
2017/01/23 Javascript
Thinkphp5微信小程序获取用户信息接口的实例详解
2017/09/26 Javascript
在 Vue 项目中引入 tinymce 富文本编辑器的完整代码
2018/05/04 Javascript
JavaScript Html实现移动端红包雨功能页面
2021/01/10 Javascript
[01:58]最残酷竞争 2016国际邀请赛中国区预选赛积分循环赛回顾
2016/06/28 DOTA
简洁的十分钟Python入门教程
2015/04/03 Python
JSONLINT:python的json数据验证库实例解析
2017/11/28 Python
Python管理Windows服务小脚本
2018/03/12 Python
python实现自动发送邮件
2018/06/20 Python
python实现简单登陆系统
2018/10/18 Python
PyTorch之图像和Tensor填充的实例
2019/08/18 Python
pytorch 预训练层的使用方法
2019/08/20 Python
python计算无向图节点度的实例代码
2019/11/22 Python
Python使用sqlite3模块内置数据库
2020/05/07 Python
Python爬虫过程解析之多线程获取小米应用商店数据
2020/11/14 Python
HTML5 Blob 实现文件下载功能的示例代码
2019/11/29 HTML / CSS
Tirendo比利时:在线购买轮胎
2018/10/22 全球购物
Talbots官网:美国成熟女装品牌
2019/11/15 全球购物
JPA面试常见问题
2016/11/14 面试题
静态成员和非静态成员的区别
2012/05/12 面试题
公司年终奖分配方案
2014/06/16 职场文书
学生上课迟到检讨书
2015/01/01 职场文书
对Golang中的FORM相关字段理解
2021/05/02 Golang