首页 > Python基础教程 >
-
爬虫(十七):Scrapy框架(四) 对接selenium爬取京东商品数据
1. Scrapy对接Selenium
Scrapy抓取页面的方式和requests库类似,都是直接模拟HTTP请求,而Scrapy也不能抓取JavaScript动态谊染的页面。在前面的博客中抓取JavaScript渲染的页面有两种方式。一种是分析Ajax请求,找到其对应的接口抓取,Scrapy同样可以用此种方式抓取。另一种是直接用 Selenium模拟浏览器进行抓取,我们不需要关心页面后台发生的请求,也不需要分析渲染过程,只需要关心页面最终结果即可,可见即可爬。那么,如果Scrapy可以对接Selenium,那 Scrapy就可以处理任何网站的抓取了。
1.1 新建项目
首先新建项目,名为scrapyseleniumtest。
scrapy startproject scrapyseleniumtest
新建一个Spider。
scrapy genspider jd www.jd.com
修改ROBOTSTXT_OBEY为False。
ROBOTSTXT_OBEY = False
1.2 定义Item
这里我们就不调用Item了。
初步实现Spider的start _requests()方法。
- # -*- coding: utf-8 -*-
- from scrapy import Request,Spider
- from urllib.parse import quote
- from bs4 import BeautifulSoup
- class JdSpider(Spider):
- name = 'jd'
- allowed_domains = ['www.jd.com']
- base_url = 'https://search.jd.com/Search?keyword='
- def start_requests(self):
- for keyword in self.settings.get('KEYWORDS'):
- for page in range(1, self.settings.get('MAX_PAGE') + 1):
- url = self.base_url + quote(keyword)
- # dont_filter = True 不去重
- yield Request(url=url, callback=self.parse, meta={'page': page}, dont_filter=True)
首先定义了一个base_url,即商品列表的URL,其后拼接一个搜索关键字就是该关键字在京东搜索的结果商品列表页面。
关键字用KEYWORDS标识,定义为一个列表。最大翻页页码用MAX_PAGE表示。它们统一定义在settings.py里面。
- KEYWORDS = ['iPad']
- MAX_PAGE = 2
在start_requests()方法里,我们首先遍历了关键字,遍历了分页页码,构造并生成Request。由于每次搜索的URL是相同的,所以分页页码用meta参数来传递,同时设置dont_filter不去重。这样爬虫启动的时候,就会生成每个关键字对应的商品列表的每一页的请求了。
1.3 对接Selenium
接下来我们需要处理这些请求的抓取。这次我们对接Selenium进行抓取,采用Downloader Middleware来实现。在Middleware中对接selenium,输出源代码之后,构造htmlresponse对象,直接返回给spider解析页面,提取数据,并且也不在执行下载器下载页面动作。
- class SeleniumMiddleware(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.
- def __init__(self,timeout=None):
- self.logger=getLogger(__name__)
- self.timeout = timeout
- self.browser = webdriver.Chrome()
- self.browser.set_window_size(1400,700)
- self.browser.set_page_load_timeout(self.timeout)
- self.wait = WebDriverWait(self.browser,self.timeout)
- def __del__(self):
- self.browser.close()
- @classmethod
- def from_crawler(cls, crawler):
- # This method is used by Scrapy to create your spiders.
- return cls(timeout=crawler.settings.get('SELENIUM_TIMEOUT'))
- def process_request(self, request, spider):
- '''
- 在下载器中间件中对接使用selenium,输出源代码之后,构造htmlresponse对象,直接返回
- 给spider解析页面,提取数据
- 并且也不在执行下载器下载页面动作
- htmlresponse对象的文档:
- :param request:
- :param spider:
- :return:
- '''
- print('PhantomJS is Starting')
- page = request.meta.get('page', 1)
- self.wait = WebDriverWait(self.browser, self.timeout)
- # self.browser.set_page_load_timeout(30)
- # self.browser.set_script_timeout(30)
- try:
- self.browser.get(request.url)
- if page > 1:
- input = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#J_bottomPage > span.p-skip > input')))
- input.clear()
- input.send_keys(page)
- time.sleep(5)
- # 将网页中输入跳转页的输入框赋值给input变量 EC.presence_of_element_located,判断输入框已经被加载出来
- input = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#J_bottomPage > span.p-skip > input')))
- # 将网页中调准页面的确定按钮赋值给submit变量,EC.element_to_be_clickable 判断此按钮是可点击的
- submit = self.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '#J_bottomPage > span.p-skip > a')))
- input.clear()
- input.send_keys(