【Python3爬虫】常见反爬虫措施及解决办法

一、UserAgent

UserAgent中文名为用户代理,它使得服务器能够识别客户使用的操作系统及版本、CPU 类型、浏览器及版本等信息。对于一些网站来说,它会检查我们发送的请求中所携带的UserAgent字段,如果非浏览器,就会被识别为爬虫,一旦被识别出来, 我们的爬虫也就无法正常爬取数据了。这里先看一下在不设置UserAgent字段时该字段的值会是什么:

import requests

url = "http://www.baidu.com"
res = requests.get(url)

解决办法:

1、收集整理常见的UserAgent以供使用

ua_list = 
    ["Mozilla/5.0 (iPod; U; CPU iPhone OS 4_3_2 like Mac OS X; zh-cn) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8H7 Safari/6533.18.5",
    "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_3_2 like Mac OS X; zh-cn) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8H7 Safari/6533.18.5",
    "MQQBrowser/25 (Linux; U; 2.3.3; zh-cn; HTC Desire S Build/GRI40;480*800)",
    "Mozilla/5.0 (Linux; U; Android 2.3.3; zh-cn; HTC_DesireS_S510e Build/GRI40) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
    "Mozilla/5.0 (SymbianOS/9.3; U; Series60/3.2 NokiaE75-1 /110.48.125 Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/413 (KHTML, like Gecko) Safari/413" ...]
2、使用第三方库--fake_useragent 使用方法如下:
from fake_useragent import UserAgent

ua = UserAgent()
for i in range(3):
    print(ua.random)
剖析:
print(ua.ie)   #随机打印ie浏览器任意版本
print(ua.firefox) #随机打印firefox浏览器任意版本
print(ua.chrome)  #随机打印chrome浏览器任意版本
print(ua.random)  #随机打印任意厂家的浏览器

二、IP

对于一些网站来说,如果某个IP在单位时间里的访问次数超过了某个阈值,那么服务器就会ban掉这个IP了,它就会返回给你一些错误的数据。一般来说,当我们的IP被ban了,我们的爬虫也就无法正常获取数据了,但是用浏览器还是可以正常访问,但是如果用浏览器都无法访问,那就真的GG了。很多网站都会对IP进行检测,比如知乎,如果单个IP访问频率过高就会被封掉。

解决办法:

使用代理IP。网上有很多免费代理和付费代理可供选择,免费代理比如:西刺代理、快代理等等,付费代理比如:代理云、阿布云等等。

三、Referer防盗链

防盗链主要是针对客户端请求过程中所携带的一些关键信息来验证请求的合法性,而防盗链又有很多种,比如Referer防盗链,还有Cookie防盗链和时间戳防盗链。

Cookie防盗链常见于论坛、社区。当访客请求一个资源的时候,他会检查这个访客的Cookie,如果不是他自己的用户的Cookie,就不会给这个访客正确的资源,也就达到了防盗的目的。时间戳防盗链指的是在他的url后面加上一个时间戳参数,所以如果你直接请求网站的url是无法得到真实的页面的,只有带上时间戳才可以。

这次的例子是天涯社区的图片分社区

mark

这里我们先打开开发者工具,然后任意选择一张图片,得到这个图片的链接,然后用requests来下载一下这张图片,注意带上Referer字段,看结果如何:

import requests

url = "http://img3.laibafile.cn/p/l/305989961.jpg"
headers = {
    "Referer": "http://pp.tianya.cn/",
    "UserAgent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36"
}
res = requests.get(url)
with open('test.jpg', 'wb') as f:
    f.write(res.content)
我们的爬虫正常运行了,也看到生成了一个test.jpg文件,先别急着高兴,打开图片看一下:

mark

解决办法:

既然他说仅供天涯社区用户分享,那我们也成为他的用户不就行了吗?二话不说就去注册了个账号,然后登录,再拿到登录后的Cookie:

注意:Cookie是有时效性的,具体多久就会失效我没测试。紧接着把Cookie添加到代码中,然后运行,可以看到成功把图片下载下来了;

搞了这么久才下了一张图片,我们怎么可能就这么满足呢?分析页面可知一个页面上有十五张图片,然后往下拉的时候会看到"正在加载,请稍后":

mark

我们立马反应过来这是通过AJAX来加载的,于是打开开发者工具查看,可以找到如下内容:

mark

可以看到每个链接“?”前面的部分都是基本一样的,“list_”后面跟的数字表示页数,而“_=”后面这一串数字是什么呢?有经验的人很快就能意识到这是一个时间戳,所以我们来测试一下:

import time
import requests

t = time.time()*1000
url = "http://pp.tianya.cn/qt/list_4.shtml?_={}".format(t)
res = requests.get(url)
print(res.text)
最后编写程序并运行:
import re
import time
import requests
from fake_useragent import UserAgent

ua = UserAgent()
headers = {
    "Referer": "http://pp.tianya.cn/",
    "Cookie": "user=w=ASD9577&id=139400111&f=1; right=web4=n&portal=n; __u_a=v2.2.4; sso=r=2037194054&sid=&wsid=54DECCFD58BB393C19060A02D963FFFC; temp=k=32072170&s=&t=1553817544&b=bdacde9b28f59113991fd271bdba3f65&ct=1553817544&et=1556409544; temp4=rm=0e74c53c8e0cfe3ec18596583e42c38f; ty_msg=1553817664458_139400111_2_0_0_0_0_0_2_0_0_0_0_0; bbs_msg=1553817664463_139400111_0_0_0_0; time=ct=1553817677.647",
    "UserAgent": ua.random
}


def crawl(url):
    res = requests.get(url, headers=headers)
    res.encoding = "utf-8"
    print("status_code:", res.status_code)
    # print("res.text:", res.text)
    if res.status_code == 200:
        # result = re.findall(r'src=".*?" alt=".*?"', res.text)
        result = re.findall('<img.+src="(.*?)" alt="(.*?)".+>', res.text)
        print(result)
        for i in result:
            # print("iiiiiiiiiii", i[0], i[1])
            download(i[0], i[1])
    else:
        print("Error Request!")


def download(href, name):
    try:
        res = requests.get(href, headers=headers)
        with open('{}.jpg'.format(name), 'wb') as f:
            f.write(res.content)
            print('[INFO]{}.jpg已下载!'.format(name))
    except:
        print("Error Download!")


if __name__ == '__main__':
    for num in range(1, 3):  # 最大页数2页
        time.sleep(2)
        t = int(time.time() * 1000)  # 获取13位时间戳
        print(t)
        page_url = "http://pp.tianya.cn/qt/list_{}.shtml?_={}".format(num, t)  # 构造链接
        crawl(page_url)