不管怎么对 selenium 的 webdriver 标识进行防御,selenium 爬取还是被识别了。。原因是 selenium 唤起浏览器需要通过 chromedriver.exe / geckodriver.exe 来唤起 谷歌 / 火狐,恰恰就是这些驱动导致 selenium 被识别,好家伙,反爬直接给你釜底抽薪了。网上也有去掉 webdriver 标识并反编译的 chromedriver.exe,但对于拿来党来说,系统环境、版本限制太大了,又不想花时间去搞这个反编译,就去找哪个能代替 selenium。福音来了~ puppeteer 简直就是为 chrome 浏览器自动化测试量身打造啊~~ pyppeteer 就是 python 版的 puppeteer。
安装和部署 pyppeteer 的教程网上有,这里不多说。
废话不多说,直接上 pyppeteer 的 scrapy 应用吧~
import pyppeteer
import asyncio
import logging
import random
import json
pyppeteer_level = logging.WARNING
logging.getLogger('pyppeteer').setLevel(pyppeteer_level)
logging.getLogger('websockets.protocol').setLevel(pyppeteer_level)
pyppeteer_logger = logging.getLogger('pyppeteer')
pyppeteer_logger.setLevel(logging.WARNING)
# 以上 代码作用是除了WARNING报错,浏览器操作就不显示在控制台了,控制台清爽多了~~
pyppeteer.DEBUG = False
# 一定一定要加下面代码,否则还是会反爬被识别出是自动化测试的
pyppeteer.launcher.AUTOMATION_ARGS.remove("--enable-automation")
class ProxyMiddleware(object):
def random_region(self, start, end):
return int(random.uniform(start, end) * 1000)
async def use_pptr(self, request, ip):
res, _ua = ({'state': False}, '')
try:
settings = get_project_settings()
driver = await pyppeteer.launch(
{'executablePath': settings.get('CHROME_PATH'), 'userDataDir': settings.get('USER_DATA_PATH'),
'headless': False, 'dumpio': True, 'autoClose': False,
'args': ['--no-sandbox', '--disable-extensions', '--disable-dev-shm-usage', '--disable-setuid-sandbox',
'--incognito', '--disable-infobars', '--disable-gpu', '--disable-popup-blocking',
'--ignore-certificate-error', '--mute-audio', '--user-agent=' + _ua, '--proxy-server=' + ip]})
page = (await driver.pages())[0] # 这样就不用非得newPage才能goto了,对于隐私模式来说,用newPage,之前launch设置的浏览器配置就全没了
except Exception as e:
print('浏览器创建失败,重新请求')
print(e)
return res
try:
await page.goto(request.url, {'waitUntil': 'networkidle2'})
await page.waitFor(self.random_region(2, 4))
query = await page.waitForSelector('#xxx div a')
await query.click() # 元素点击
input = await page.waitForSelector('.xxx')
await input.focus() # 元素焦点获取
await input_into.type(str(request.meta['xxx'])) # 输入内容
search = await page.waitForSelector('#xxx')
await search.click() # 提交查询
await page.waitFor(self.random_region(2, 3)) # 等待2-3秒,等待新窗口打开
page1 = (await driver.pages())[1] # 新页面
link_a = await page1.waitForXPath('//xxx')
ele = await page1.Jeval('.xxx', 'el=> el.getAttribute(\'id\')') # 获取指定元素的指定id属性值
res = {'state': True, 'data': json.dumps(await page1.content()) # 页面源码
pass
except Exception as e:
print(e)
finally:
await page.waitFor(1000) # 关闭浏览器前一定要有个等待缓冲时间,否则有时会报错哦
await driver.close()
return res.
def process_request(self, request, spider):
ip_info = 'http://xxx.xxx.xxx.xxx'
task = asyncio.ensure_future(self.use_pptr(request, ip_info))
asyncio.get_event_loop().run_until_complete(task)
result = task.result()
return HtmlResponse(url=request.url, body=result['data'], request=request, encoding='utf-8', status=200) if result['state'] else HtmlResponse(url=request.url, status=400, request=request)
目标网站服务器维护不易,让我们自觉放慢爬取脚步吧,不让爬虫给服务器太大压力哦~