不会JS逆向也能高效结合Scrapy与Selenium实现爬虫抓取
1. 创建基础的scrapy项目
1.1 基础项目
- 在pycharm中安装scrapy框架
pip install scrapy
- 创建项目
scrapy startproject 项目名称
我们现在可以看到整体文件的目录:
firstBlood
├── firstBlood # 项目跟目录
│ ├── init.py
│ ├── items.py # 封装数据的格式
│ ├── middlewares.py # 所有中间件
│ ├── pipelines.py # 所有的管道
│ ├── settings.py # 爬虫配置信息
│ └── spiders # 爬虫文件夹, 稍后里面会写入爬虫代码
│ └── init.py
└── scrapy.cfg # scrapy项目配置信息
- 创建爬虫文件
cd firstBlood
- 创建主文件
scrapy genspider 爬虫文件的名称(自定义一个名字即可) 起始url (随便写一个网址即可)
比如:
scrapy genspider baidu www.baidu.com
1.2 修改配置文件
打开settings.py:
-
- 不遵从robots协议:ROBOTSTXT_OBEY = False
- 指定输出日志的类型:LOG_LEVEL = ‘ERROR’
- 指定UA:USER_AGENT = ‘xxxxxx’
1.3 启动程序
scrapy crawl 文件名(我这里是baidu)
2. Scrapy + Selenium
我们现在来进行基础的数据采集,我这里准备采集有关“三只羊”的资讯信息和详情内容:
2.1 通过scrapy异步爬取url
主文件baidu.com:
import scrapy
from ..items import FirstItem
class BaiduSpider(scrapy.Spider):
name = "baidu"
allowed_domains = ["www.baidu.com"]
start_urls = [
'http://www.baidu.com/s?ie=utf-8&medium=0&rtt=1&bsst=1&rsv_dl=news_t_sk&cl=2&wd=%E4%B8%89%E5%8F%AA%E7%BE%8A&tn=news&rsv_bp=1&oq=&rsv_sug3=12&rsv_sug1=5&rsv_sug7=101&rsv_sug2=0&rsv_btype=t&f=8&inputT=1395&rsv_sug4=1395']
def parse(self, response):
text = response
# 获取数据
contents = text.xpath('//a[@class="news-title-font_1xS-F"]')
for c in contents:
url = c.xpath('@href').extract_first()
title = c.xpath('@aria-label').extract_first()
items = FirstItem()
items['url'] = url
items['title'] = title
yield scrapy.Request(url=url, callback=self.detail_parse, meta={'item': items})
def detail_parse(self, response):
print(response)
items:
import scrapy
class FirstItem(scrapy.Item):
url = scrapy.Field()
title = scrapy.Field()
2.2 中间件结合selenium
假设现在,详情页面是涉及到js加密的,这该如何解决呢?
聪明的小伙伴已经想到了:是selenium!
只要模拟的好,所见即所得
那么我们该如何做呢?
众所周知,在Scrapy框架中,有一种名叫中间件的东西,他有什么用呢:
此中间件可以在请求之前、响应之前截取请求/响应信息,并在此做一系列操作
也就是说,在返回给detail_parse
之前,我们可以重新处理响应数据
来看代码:
baidu.py
class BaiduSpider(scrapy.Spider):
name = "baidu"
# allowed_domains = ["www.baidu.com"]
start_urls = [
'http://www.baidu.com/s?ie=utf-8&medium=0&rtt=1&bsst=1&rsv_dl=news_t_sk&cl=2&wd=%E4%B8%89%E5%8F%AA%E7%BE%8A&tn=news&rsv_bp=1&oq=&rsv_sug3=12&rsv_sug1=5&rsv_sug7=101&rsv_sug2=0&rsv_btype=t&f=8&inputT=1395&rsv_sug4=1395']
# 实例化浏览器对象
bro = webdriver.Chrome()
def parse(self, response):
text = response
# 获取数据
contents = text.xpath('//a[@class="news-title-font_1xS-F"]')
items = FirstItem()
for c in contents:
url = c.xpath('@href').extract_first()
title = c.xpath('@aria-label').extract_first()
items['url'] = url
items['title'] = title
yield scrapy.Request(url=url, callback=self.detail_parse, meta={'item': items} , dont_filter=True)
def detail_parse(self, response):
item = response.meta['item']
content = response.xpath('//div[@class="EaCvy"]//text()').extract_first()
item['content'] = content
yield item
# 重写父类方法
def closed(self, spider):
print('整个操作结束!!!')
self.bro.quit()
重点:
-
bro = webdriver.Chrome()
这里实例化了一个浏览器对象 -
这里是解析完url后,再一次手动发起请求
yield scrapy.Request(url=url, callback=self.detail_parse, meta={'item': items} , dont_filter=True)
- 这是重写的父类方法
def closed(self, spider):
print('整个操作结束!!!')
self.bro.quit() # 执行完就关闭浏览器
最重要的是中间件的写法:
pipelines.py
import time
from scrapy import signals
from itemadapter import is_item, ItemAdapter
from scrapy.http import HtmlResponse
class FirstDownloaderMiddleware:
def process_request(self, request, spider): # 这里的spider就是指的主对象程序,也就是前面发出请求的类对象
return None
def process_response(self, request, response, spider):
# 第一次请求不触发selenium
if request.url != spider.start_urls[0]:
bro = spider.bro # selenium浏览器对象
bro.get(request.url)
time.sleep(3)
source = bro.page_source
# 重新封装一个响应对象
new_response = HtmlResponse(url=request.url, body=source, request=request, encoding='utf-8')
return new_response
return response
items.py
import scrapy
class FirstItem(scrapy.Item):
url = scrapy.Field()
title = scrapy.Field()
content = scrapy.Field()
3. 总结
其实想要实现scrapy和selenium共同完成爬虫任务还是比较简单的。
最重要的是理解中间件的性质,并且能够在合适的地方去修改请求/响应内容。
在遇到比较复杂的页面时,可以尝试使用scrapy + selenium的模式进行爬取。
将两者结合使用,能够弥补彼此的不足,发挥出更大的优势。