Pyppeteer的安装:
pip install pyppeteer
清华源安装:
pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple pyppeteer
Pyppeteer可以直接饶过网站加密,从加载完的浏览器中直接抓取
本次爬取的网站: Scrape Center spa6
1.网站检查:
F12 检查页面 ——> 点击network,切换页面,发现加载出部分数据,点击第一条数据查看headers
Request URL:
https://spa6.scrape.center/api/movie?limit=10&offset=10&token=MjEzMmE3MTIxOTdjNzEzNWQyMzc5OWVmN2U1MjYxNGYxZmRkNWM0ZSwxNjU1ODYwOTEw
这一串为加密字符:token=MjEzMmE3MTIxOTdjNzEzNWQyMzc5OWVmN2U1MjYxNGYxZmRkNWM0ZSwxNjU1ODYwOTEw
由此可知,网站数据是由api调用,并进行了加密,若没学习过Javascript逆向,则可以使用Pyppeteer调用;
2.项目实施:
2.1 Scrapy项目创建:
创建Scrapy项目:
- 在终端中找到需要创建项目的目录;
scrapy startproject spa6
spa6为项目名,scrapy startproject为固定语法;- 进入到spa6文件夹中,
cd spa6
scrapy genspider example example.com
后面两随便写即可
2.2 项目准备:
在Scrapy项目创建过程中,我写的第一个example是app1,所以在文件夹spider中就可以看见一个app1.py文件,点开它
这就是项目的主文件,需要进行爬取的操作均在这里实现;
在app1.py导入Pyppeteer库,from gerapy_pyppeteer import PyppeteerRequest
import scrapy
from gerapy_pyppeteer import PyppeteerRequest
class App1Spider(scrapy.Spider):
name = 'app1'
# allowed_domains = ['df']
# start_urls = ['http://df/']
def start_requests(self):
url = "https://spa6.scrape.center/page/2"
# 使用Pyppeteer 等待class = m-b-sm元素出现
yield PyppeteerRequest(url, callback=self.show, wait_for='.m-b-sm')
def show(self, response):
print(response.text)
关键代码:
yield PyppeteerRequest(url, callback=self.show, wait_for='.m-b-sm')
参数一:用于传递url;
参数二:指定传递数据的函数;这里指定的是show函数;
参数三:等待一个元素加载出来,这里等待的是class类 .m-b-sm
此时还需要做另一些准备,将Pyppeteer与Scrapy对接
项目关键:
在setting.py文件夹中
更改 DOWNLOADER_MIDDLEWARES
实现Pyppeteer的对接middlewares,使用Pyppeteer中的下载器
DOWNLOADER_MIDDLEWARES = {
# 'spa6.middlewares.Spa6DownloaderMiddleware': 543,
'gerapy_pyppeteer.downloadermiddlewares.PyppeteerMiddleware': 543,
}
# 指定不加载的数据类型,image图片,font字体文件
GERAPY_PYPPETEER_IGNORE_RESOURCE_TYPES = ['image', 'font']
末尾加入
# 对接pyppeteer
CONCURRENT_REQUESTS = 3
GERAPY_PYPPETEER_HEADLESS = False
# 反屏蔽
GERAPY_PYPPETEER_PRETEND = True
# 设置超时时间
GERAPY_PYPPETEER_DOWNLOAD_TIMEOUT = 30
# 设置窗口大小
GERAPY_PYPPETEER_WIDTH = 1400
GERAPY_PYPPETEER_HEIGHT = 700
Scrapy的运行
需要在终端中输入 scrapy crawl 项目名
,项目名就是app1.py文件中的name参数,我这里就是app1;
接下来介绍,一种更加方便的方法,就不用每次都在终端中输入直接运行就可以了
创建一个启动文件,run.py
run.py文件代码,要启动Scrapy项目直接运行run.py即可
from scrapy.cmdline import execute
execute(['scrapy', 'crawl', 'app1'])
2.3 项目流程:
一下代码是在app1.py主文件中的show()
函数内
导入lxml库中的etree,from lxml import etree
该代码检查是否可以获得数据,由于是异步执行,所以获取数据的顺序不一定一致;
def show(self, response):
source = response.text
demo = etree.HTML(source).xpath('//h2[@class="m-b- sm"]/text()')
print(demo)
即可测试能否爬取到数据
项目翻页有两种方法实现,方法一是直接通过更改网页链接进行翻页;
方法二是通过Pyppeteer的鼠标控制进行翻页;
方法一:使用for循环翻页
import scrapy
from gerapy_pyppeteer import PyppeteerRequest
from lxml import etree
class App1Spider(scrapy.Spider):
name = 'app1'
# allowed_domains = ['df']
# start_urls = ['http://df/']
def start_requests(self):
# 翻页
for i in range(1, 4):
url = "https://spa6.scrape.center/page/{}".format(i)
yield PyppeteerRequest(url, callback=self.show, wait_for='.m-b-sm')
# 使用Pyppeteer 等待class = m-b-sm元素出现
def show(self, response):
source = response.text
# 获取页面中10个div[@class="el-card__body"]
demo = etree.HTML(source).xpath('//div[@class="el-card__body"]')
for i in demo:
# 获取标题
title = i.xpath('div[1]/div[2]/a/h2/text()')[0]
category = i.xpath('div[1]/div[2]/div[1]//text()')
# category是一个列表,所以使用map函数解析列表,并执行操作;str.strip消除空格;再用|拼接
category = '|'.join(map(str.strip, category))
print(title)
print(category)
方法二:使用点击控制翻页
需要载入一个异步包 asyncio
在类外创建一个异步函数
import asyncio
async def hello(page):
await page.click('.btn-next')
def start_requests(self):
# 翻页
url = "https://spa6.scrape.center/page/1"
yield PyppeteerRequest(url, callback=self.show, wait_for='.m-b-sm', actions=hello)
翻页:yield PyppeteerRequest(url, callback=self.show, wait_for='.m-b-sm', actions=hello)
actions = hello为调用hello函数
翻页的思路是:设定一个类参数page,用于记录翻页情况,没翻一次减去一次page,便于指定爬取页数
import scrapy
from gerapy_pyppeteer import PyppeteerRequest
from lxml import etree
import asyncio
async def hello(page):
await page.click('.btn-next')
class App1Spider(scrapy.Spider):
name = 'app1'
# allowed_domains = ['df']
# start_urls = ['http://df/']
page = 10
def start_requests(self):
# 翻页
url = "https://spa6.scrape.center/page/1"
yield PyppeteerRequest(url, callback=self.show, wait_for='.m-b-sm')
def show(self, response):
print(response.url)
source = response.text
# 获取页面中10个div[@class="el-card__body"]
demo = etree.HTML(source).xpath('//div[@class="el-card__body"]')
for i in demo:
# 获取标题
title = i.xpath('div[1]/div[2]/a/h2/text()')[0]
category = i.xpath('div[1]/div[2]/div[1]//text()')
# category是一个列表,所以使用map函数解析列表,并执行操作;str.strip消除空格;再用|拼接
category = '|'.join(map(str.strip, category))
print(title, category)
App1Spider.page = App1Spider.page - 1
if App1Spider.page > 0:
yield PyppeteerRequest(response.url, callback=self.show, wait_for='.m-b-sm', actions=hello, dont_filter=True)
yield PyppeteerRequest(response.url, callback=self.show, wait_for=‘.m-b-sm’, actions=hello, dont_filter=True),
该处加入了dont_filter
参数
Scrapy中是默认开启过滤,默认dont_filter = False
我们第一次传入url时已经使用过了response.url,当我们进行函数回调时,系统会把重复的url删除,这样就不能进行回调了,所以要把回调机制关闭