Scrapy反爬虫之521异常

引子

最近在爬取一个网站时, 遇到了521错误, 这是一种网站的反爬技术, 浏览器会渲染很多东西, 代码爬数据会漏掉浏览器渲染的信息

思路

可以尝试复制浏览器的cookie信息, 加在请求头中, 但是这样只能获取单个域名的网页。恰巧我需要爬取的网站下面有多个二级域名的网页(二级域名网页的链接可以通过一级域名获取), 复制每个二级域名的cookie来爬取每个二级域名的网页是不太可能的

进一步的方案是通过PhantomJS的无头浏览器发送两次请求, 第一次请求获取一部分Cookie(__jsl_clearance)的信息, 第二次请求再将这部分Cookie加在Header信息中, 就可以获得网页内容

第一次尝试

urls = request.url.split("/")
host = urls[-2]

headers = {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
    "Accept-Language": "zh-CN,zh;q=0.8,en;q=0.6",
    "Connection": "keep-alive",
    "Host": host,
    "Referer": request.url,
    "Upgrade-Insecure-Requests": 1
}
ua = random.choice(spider.settings.get("UAPOOL"))
headers["User-Agent"] = ua

cap = DesiredCapabilities.PHANTOMJS.copy()
for key, value in headers.items():
    cap['phantomjs.page.customHeaders.{}'.format(key)] = value
driver = webdriver.PhantomJS(desired_capabilities=cap)
driver.get(request.url)
cj = driver.get_cookies()

上面的代码, 构造请求的头信息, 通过PhantomJS发起第一次请求, 获得cookies

cookie = ''
for c in cj:
    cookie = cookie + c['name'] + '=' + c['value'] + ';'
cookie = cookie[:-1]
headers['Cookie'] = cookie

dcap = DesiredCapabilities.PHANTOMJS.copy()
for key, value in headers.items():
    dcap['phantomjs.page.customHeaders.{}'.format(key)] = value

time.sleep(10)
driver2 = webdriver.PhantomJS(desired_capabilities=dcap)
driver2.get(request.url)
content = driver2.page_source.encode('utf-8')
return HtmlResponse(request.url, encoding='utf-8', body=content, request=request)

通过第一次请求返回的cookie, 构造头部的Cookie, 并发起第二次请求, 注意第一次和第二次发起的请求的头部信息相同, 只是第二次加入了Cookie的信息

通过发起二次请求的方式可以获取到网页的内容, 但是这种方式不太稳定, 经常连续发送几次就会有一段时间获取不到网页, 让人感觉很崩溃。于是尝试过滤掉这部分拉取不到的请求, 将这部分请求的url记录在数据库, 并重复爬取。但又遇到一个新的问题, PhantomJS连续拉取到一定的次数, 就会长时间的没有响应, 加超时时间也不管用

将PhantomJS和Scrapy整合在一起会导致网页一次都拉取不到, 猜想可能因为PhantomJS是一种串行的爬取方式, 而Scrpay是一种异步的拉取的方式, 这两种方式有冲突。

第二次尝试

通过第一次尝试得出两个结论, 第一: PhantomJS不太可靠, 网上得知PhantomJS已经不再维护, 于是考虑使用PhantomJS的替代品Chrome Driver. 第二: Scrapy多线程的拉取方式和PhantomJS/Chrome Driver无头的串行拉取方式没法融合. 于是想到单独使用Phython写一个原生的爬虫

这里只共享通过Chrome Driver + selenium解决521反爬的方案

headers = {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
    "Accept-Language": "zh-CN,zh;q=0.8,en;q=0.6",
    "Connection": "keep-alive",
    "Upgrade-Insecure-Requests": 1
}
ua = random.choice(UAPOOL)
headers["User-Agent"] = ua
host = url.split("/")[2]
headers["Host"] = host
headers["Referer"] = url

cj = Content.get_cookie(url, ua)

get_cookie方法

chrome_options = Options()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')
user_agent = 'user-agent='+ua
chrome_options.add_argument(user_agent)
driver = webdriver.Chrome(chrome_options=chrome_options, executable_path="/home/bearyb/chromedriver")

try:
    driver.get(url)
    cj = driver.get_cookies()
    driver.quit()
    return cj
except TimeoutException:
    print("first time get " + url + " timeout")
    driver.quit()
    return ''

还是和之前一样, 构建第一次请求通过get_cookie获取cookie, 可以看到在get_cookie方法里已经将PhantomJS换成了chrome driver的headless方式

cookie = '_ga=GA1.2.413717745.1574286560;UM_distinctid=16e8aca983f9-09c74e905834908-76256753-13c680' \
         '-16e8aca984088;Hm_lvt_dfa5478034171cc641b1639b2a5b717d=1576274996,1576361248,1576673944,' \
         '1576794342;g=HDF.145.5deb431611b32;_gid=GA1.2.742943599.1576794342;CNZZDATA-FE=CNZZDATA-FE;' \
         'Hm_lpvt_dfa5478034171cc641b1639b2a5b717d=1576800627;' \
         'CNZZDATA1256706712=' + '36179960-1576796605-https%253A%252F%252F' + host + '%252F%7C1576796605;'

for c in cj:
    cookie = cookie + c['name'] + '=' + c['value'] + ';'
cookie = cookie[:-1]

headers['Cookie'] = cookie
return Content.get_content(url, headers)

get_content方法

def get_content(url, headers):
    dcap = DesiredCapabilities.PHANTOMJS.copy()
    for key, value in headers.items():
        dcap['phantomjs.page.customHeaders.{}'.format(key)] = value
    time.sleep(10)

    session = requests.Session()
    html = session.get(url, headers=headers).content.decode('gbk')
    content = etree.HTML(html)
    return content

将第一次请求的Cookie加入到header中再次请求, 就能获得返回的网页, 第二次请求没有使用Chrome的请求方式, 而是使用了python框架的requests模块

后话:

所谓道高一尺, 魔高一丈, 这个问题前前后后一直困扰了我两个多星期, 不断的尝试, 不断的失败, 不停的调整方向, 还好最后终于解决了问题. 在这里分享给大家解决521反爬的思路, 望学习的道路上共同进步!

发布了69 篇原创文章 · 获赞 8 · 访问量 9393

猜你喜欢

转载自blog.csdn.net/u011414629/article/details/103652442