一直以来,我们都是直接使用 scrapy 框架的 Request 模块进行网页数据的请求。但是如果网页中有动态加载的数据,这种方式就不容易实现了。
其实 scrapy 更多的处理的还是没有动态加载数据的页面。对于动态加载的页面,我们还是比较倾向于使用 requests。
但是如果真的有这么个需求,需要我们使用 scrapy 爬取动态页面的话,通过 selenium 发送请求获取数据,将会是一个不错的选择。
接下来,我们通过爬取网易新闻,来演示如何在 scrapy 中,使用 selenium 爬取数据。
需求:爬取网易新闻中的国内,国际,军事,航空,无人机这五个板块下所有的新闻数据(标题+内容)
网址 url:https://news.163.com/
分析:
- 首页没有动态加载的数据,可以直接爬取到五个板块对应的 url
- 每一个板块对应的页面中的新闻标题是动态加载,需要使用 selenium 爬取新闻标题和详情页的 url(关键)
- 每一条新闻详情页面中的数据不是动态加载,在这里可以爬取到新闻内容
selenium 在 scrapy 中的使用流程
- 在爬虫类中实例化一个浏览器对象,将其作为爬虫类的一个属性
- 在中间件中实现浏览器自动化相关的操作
- 在爬虫类中重写
closed(self, spider)
方法,在其内部关闭浏览器对象
接下来,我们就按照流程,依次编写我们的代码。
首先,编写爬虫源文件中的代码,一切都按照正常思路走就行。先从首页到每个模块页,在模块页中拿到新闻的标题和详情页的 url。再从模块页进入到详情页,拿到文章内容。
用代码表示就是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| import scrapy from selenium import webdriver from wangyiPro.items import WangyiproItem
class WangyiSpider(scrapy.Spider): name = 'wangyi' start_urls = ['http://news.163.com/'] module_urls = [] bro = webdriver.Chrome()
def parse(self, response): target_list = [3, 4, 6, 7, 8] li_list = response.xpath('//div[@class="ns_area list"]/ul/li') for target in target_list: li = li_list[target] url = li.xpath('./a/@href').extract_first() self.module_urls.append(url) yield scrapy.Request(url, callback=self.parse_module)
def parse_module(self, response): div_list = response.xpath('//div[@class="newsdata_wrap"]/ul/li[1]/div/div') for div in div_list: url = div.xpath('./a/@href').extract_first() title = div.xpath('./div/div[1]/h3/a/text()').extract_first() if url and title: item = WangyiproItem() item['title'] = title yield scrapy.Request(url, callback=self.parse_detail, meta={'item': item})
def parse_detail(self, response): """解析新闻详情""" item = response.meta['item'] content = response.xpath('//div[@id="endText"]/p/text()').extract() item['content'] = content yield item
def closed(self, spider): self.bro.close()
|
但是这样是解析不出数据来的。因为我们前面分析过,模块页中的每个文章都是动态加载的。我们需要修改响应数据,让响应数据变成我们想要的那种,加载好了新闻链接和标题的网页数据。这就要在中间件中,通过 selenium 发送请求,获取数据了。
用代码实现就是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| from scrapy.http import HtmlResponse from time import sleep
class WangyiproDownloaderMiddleware(object): def process_response(self, request, response, spider): if request.url in spider.module_urls: bro = spider.bro bro.get(request.url) sleep(2) bro.execute_script('window.scrollTo(0, document.body.scrollHeight)') sleep(2) return HtmlResponse(url=request.url, body=bro.page_source, encoding='utf-8', request=request) return response
|
剩下的就是在配置中开启管道和中间件,在 items 中写上相应字段,在管道中进行数据持久化存储,就不一一介绍了。
至此,我们实现了在 scrapy 中使用 selenium 发送请求。