前言
在爬取网站数据,往往会碰到一些加密的数据或者各种各样的验证码,可是 直接使用 request 需要js 逆向耗费大量时间,但使用 selemunim 等自动化工具又很容易被识别。哪有没有一种折中的办法既可以有request 请求的速度又有selemunim 的简单的操作?或许DrissionPage 可以帮助你
一、DrissionPage 是什么?
概述:DrissionPage 是一个基于 python 的网页自动化工具。它既能控制浏览器,也能收发数据包,还能把两者合而为一。可兼顾浏览器自动化的便利性和 requests 的高效率。它功能强大,内置无数人性化设计和便捷功能。它的语法简洁而优雅,代码量少,对新手友好。
本库采用全自研的内核,内置了 N 多实用功能,对常用功能作了整合和优化,对比 selenium,有以下优点:
- 无 webdriver 特征
- 无需为不同版本的浏览器下载不同的驱动
- 运行速度更快
- 可以跨 iframe 查找元素,无需切入切出
- 把 iframe 看作普通元素,获取后可直接在其中查找元素,逻辑更清晰
- 可以同时操作浏览器中的多个标签页,即使标签页为非激活状态,无需切换
- 可以直接读取浏览器缓存来保存图片,无需用 GUI 点击另存
- 可以对整个网页截图,包括视口外的部分(90以上版本浏览器支持)
- 可处理非
open
状态的 shadow-root
更多用法和概述不在此在说明,详细用法点击这里
二、scrapy + DeissionPage 爬取51 job
准备工具:
pycharm 3.7
scrapy 2.9.0
DrissionPage 3.2.30
1.创建scrapy 项目
在pycharm 终端依次输入:
scrapy startproject _51job
cd _51job
scrapy genspider a_51job www.51job.com
创建好的文件如下:
2. 重写 middewares.py
因为 scrapy 是使用 request 发送请求的,因此需要重写中间件来使用DrissionPage
class DrissionPageMiddleware:
def process_request(self, request, spider):
"""
使用 Drissonpage 请求
:param request:
:param spider:
:return:
"""
url = request.url
spider.edge.get(url) # 请求页面,和selemunim get方法一致
spider.edge.wait.load_start(3) # 等待加载
html = spider.edge.html # 获取页面 html,和selemunim的 source 属性一致
return HtmlResponse(url=url, body=html, request=request, encoding='utf-8')
def process_response(self, request, response, spider):
"""
处理滑块验证码
:param request:
:param response:
:param spider:
:return:
"""
url = request.url
from DrissionPage.common import ActionChains
while spider.edge.s_ele('#nc_1_n1z'): # 出现滑块验证码
spider.edge.clear_cache() # 清理缓存
ac = ActionChains(spider.edge)
for i in range(random.randint(10, 20)):
ac.move(random.randint(-20, 20), random.randint(-10, 10))
else:
ac.move_to('#nc_1_n1z')
ac.hold().move(300)
time.sleep(2)
spider.edge.get(url)
time.sleep(2)
html = spider.edge.html
if not '滑动验证页面' in html: # 验证成功
return HtmlResponse(url=url, body=html, request=request, encoding='utf-8')
else: # 验证失败
spider.edge.clear_cache()
spider.edge.get(url)
return response
3. 编写 a_51job.py
import time
import scrapy
from DrissionPage import ChromiumPage
import random
from DrissionPage.errors import ElementNotFoundError
class A51jobSpider(scrapy.Spider):
name = "_51job"
# allowed_domains = ["www.51job.com"]
start_urls = [] # 搜索链接 如:https://we.51job.com/pc/searchjobArea=190200&keyword=java&searchType=2&sortType=0&metro=
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.edge = ChromiumPage() # 实例化浏览器
self.all_urls = []
self.isdatail = False # 搜索页基本信息
self.isbasic = True # 详细页信息
def get_all_want_info(self, response):
"""
获取所有的招聘信息
:return:
"""
last_page = int(self.edge.eles('.number')[-1].text) # 搜索页总页数
for page_num in range(1, last_page + 1): # 翻页
print(f'------正在爬取第{page_num}页--------')
self.edge.ele('#jump_page').input(page_num) # 输入页码
self.edge.ele('.jumpPage').click() # 点击跳转
time.sleep(3)
# 获取招聘基本信息
if self.isbasic:
page_want_info = []
job_names = self.edge.s_eles('.jname at') # 职业名称
times = self.edge.s_eles('.time') # 发布时间
sals = self.edge.s_eles('.sal') # 薪资
requrire = self.edge.s_eles('.d at') # 招聘要求
tags = self.edge.s_eles('.tags') # 福利
company_names = self.edge.s_eles('.cname at') # 公司
company_class = self.edge.s_eles('.dc at') # 公司类别
domain_class = self.edge.s_eles('.int at') # 领域
for base_want_info in zip(company_names, job_names, times, sals, tags, requrire, company_class,
domain_class):
want_info = []
for i in base_want_info:
want_info.append(i.text)
page_want_info.append(want_info)
print(page_want_info)
# 获取招聘详细信息
if self.isdatail:
for i in self.edge.s_eles('.el'): # 获取招聘url
self.all_urls.append(i.attr('href'))
random.shuffle(self.all_urls)
for url in self.all_urls: # 获取招聘信息
yield scrapy.Request(url=url, callback=self.parse)
try:
job_name = self.edge.s_ele('xpath:/html/body/div[2]/div[2]/div[2]/div/div[1]/h1').text # 招聘职位
compensation = self.edge.s_ele(
'xpath:/html/body/div[2]/div[2]/div[2]/div/div[1]/strong').text # 招聘薪资
required_info = self.edge.s_ele('.msg ltype').text # 招聘要求
address = self.edge.s_ele('xpath:/html/body/div[2]/div[2]/div[3]/div[2]/div/p').text # 上班地址
company_info = self.edge.s_ele('.tmsg inbox').text # 公司信息
print(job_name, address, compensation)
except ElementNotFoundError:
pass
self.edge.get(self.start_urls[0])
self.all_urls = []
def start_requests(self):
yield scrapy.Request(
url=self.start_urls[0],
callback=self.get_all_want_info
)
def parse(self, response, **kwargs):
pass
from scrapy import cmdline
cmdline.execute("scrapy crawl _51job".split())

如果想要分条件搜索,直接控制浏览器点击即可,此处不在赘述
总结
本文使用 scrapy + DrissionPage 爬取 51job 招聘信息,因为DrissionPage 打开的是原有的浏览器驱动,无需下载其他驱动并且能任何自动化特征值,遇到51job滑块验证码的通过率几乎在100%。爬取速度也不错,是个强大又简单的工具。