scrapy对接selenium爬取动态渲染页面
一、马蜂窝热门游记信息爬取
爬取马蜂窝 热门游记信息(标题、内容、地点、作者)
分析其网页,在源代码中可以发现没有这些想要的信息,初步判定这是用js渲染出来的。
再在network中也没有找到想要的信息。因此选择使用selenium对页面操作拿到想要的数据。
在网页源代码中未找到主页面中的词语等,
二、对接selenium
在爬虫代码中加入selenium所需的内容
class MafengwoSpider(scrapy.Spider):
name = 'mafengwo'
# allowed_domains = ['xxxx.com']
start_urls = ['http://www.mafengwo.cn/']
options = Options()
options.add_experimental_option('debuggerAddress', '127.0.0.1:9222')
browser = webdriver.Chrome(options=options)
由于网页会检测到使用selenium 添加option规避检测
在运行代码前需要在终端输入:
chrome.exe --remote-debugging-port=9222 --user-data-dir='一个任意空文件夹'
开启一个谷歌浏览器,当执行爬虫时会接管这个开启的浏览器,规避系统检测
再开启下载中间件,修改process_response方法拦截响应:
先导入scrapy自带的模块将修改好的响应返回
from scrapy.http import HtmlResponse
from time import sleep
def process_response(self, request, response, spider):
list_page = []
# 通过spider可以获取到爬虫代码中的一些类属性
browser = spider.browser
browser.get(request.url)
# browser.maximize_window()
sleep(1)
# 执行js脚本向下滚动一个页面
browser.execute_script('window.scrollTo(0,document.body.scrollHeight)')
sleep(3)
# 这里得到当前页面的内容
page = browser.page_source
# 由于页面有9页并且这些页面都不会改变网页url
# 所以通过循环点击下一页的操作将页面所有内容存入一个列表中
list_page.append(page)
for i in range(9):
click = browser.find_element_by_xpath('//*[@id="_j_tn_pagination"]/a[last()]')
click.click()
sleep(3)
page = browser.page_source
list_page.append(page)
# 由于返回的页面的内容不能是列表通过join方式把得到的页面内容拼接到一起
list_page = ''.join(list_page)
return HtmlResponse(request=request, body=list_page, encoding='utf-8', url=request.url)
三、解析页面信息得到需要内容
如果不需要多次请求同一url 可以不写dont_filter默认False,如果不开启多次请求同一url会被过滤掉只请求一次
def start_requests(self):
for url in self.start_urls:
yield scrapy.Request(url, callback=self.parse, dont_filter=True)
def parse(self, response):
divs = response.xpath('//*[@id="_j_tn_content"]/div[1]')
for div in divs:
for item in div.xpath('./div'):
items = MafengwospiderItem()
# 由于有些标题内容与其他不一致,可能提取到的标题不是想要的,或者没有该内容
# 用判断和try过滤避免报错
title = item.xpath('./div[2]/dl/dt/a/text()').extract()
if title:
try:
title = title[1]
except Exception:
title = title[0]
text = item.xpath('./div[2]/dl/dd/a/text()').re(r'\S')
text = ''.join(text)
address = item.xpath('./div[2]/div/span[2]/a/text()').extract_first()
# 由于作者名有字体反爬,因此采用正则提取
author = item.xpath('./div[2]/div/span[3]/a/text()').re(r'\S')
author = ''.join(author)
items['title'] = title
items['author'] = author
items['address'] = address
items['text'] = text
yield items
在马蜂窝网页中作者名会携带很多\n和空格通过正则过滤掉非空格内容再列表拼接得到作者名
(对于这种有很多空格换行符的文本信息,可以采用正则来过滤掉)
完整代码:
爬虫代码:
import scrapy
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from mafengwoSpider.items import MafengwospiderItem
class MafengwoSpider(scrapy.Spider):
name = 'mafengwo'
# allowed_domains = ['xxxx.com']
start_urls = ['http://www.mafengwo.cn/']
options = Options()
options.add_argument('--headless')
options.add_experimental_option('debuggerAddress', '127.0.0.1:9222')
browser = webdriver.Chrome(options=options)
def start_requests(self):
for url in self.start_urls:
yield scrapy.Request(url, callback=self.parse, dont_filter=True)
def parse(self, response):
divs = response.xpath('//*[@id="_j_tn_content"]/div[1]')
for div in divs:
for item in div.xpath('./div'):
items = MafengwospiderItem()
title = item.xpath('./div[2]/dl/dt/a/text()').extract()
if title:
try:
title = title[1]
except Exception:
title = title[0]
text = item.xpath('./div[2]/dl/dd/a/text()').re(r'\S')
text = ''.join(text)
address = item.xpath('./div[2]/div/span[2]/a/text()').extract_first()
author = item.xpath('./div[2]/div/span[3]/a/text()').re(r'\S')
author = ''.join(author)
items['title'] = title
items['author'] = author
items['address'] = address
items['text'] = text
yield items
items:
class MafengwospiderItem(scrapy.Item):
# define the fields for your item here like:
title = scrapy.Field()
author = scrapy.Field()
address = scrapy.Field()
text = scrapy.Field()
settings设置: