近段时间, 看B站一个质量不错的免费爬虫视频学习, 但是发现 有些网站升级了反爬虫, 无法爬取了.然后我就踩了一些坑.

通过xpath 获取到的属性缺少域名部分

网站里的图片,都是有一个 src 属性, 这个属性里有 url 的, 要么给全部的url ,要么只给 网站根目录下的 url 路径.
但是我碰到一个网站, 在浏览器页面使用开发者工具的时候, 看到 src 里的url 是包括协议,域名和项目路径的, 可是通过requests 发送请求, 然后用lxml 的 etree 使用xpath 路径提取出来的结果和页面上看到的 url 相比, 缺失了协议和域名, 搞得我还以为xpath 表达式写错了, 校验后发现, 返回的就是缺失协议和域名的URL, 那么我们在 浏览器上开发者工具看到的, 可能就是网站前端自己补全的协议和域名.

人人网反爬

由于不少人看我正在看的那套视频, 导致不少人都用爬虫去爬人人网, 人人网直接升级了反爬机制了.
第一, 人人网进行了拦截, 在浏览器上, 登陆成功的时候, 发送的那个请求, 根本捕捉不了.然后我就故意输错密码, 捕获了一个登陆失败的请求, 而且这个请求被设置成阿贾克斯请求了,
第二, 我发现, 我们在页面输入密码的时候,前端已经发送阿贾克斯请求了, 并且拿到了后台返回的 token , token 是藏在cookie 里的 , 随着我们点击登陆, 发送一个阿贾克斯请求进行登录, 会把整个 cookie 给发送. 通过这种方法, 进行反爬限制.
第三, 我发现登陆请求的参数里, 有两三个字段 是每次都不一样的字符串, 我并不知道它是如何加密的, 但是猜到这个是每次只能用一次的.
第四, 人人网进行注册限制, 新用户想要注册一个账号, 然后来进行爬虫操作, 发现点击首页的注册按钮, 跳转会一直卡住, 无法注册, 只能用微信或者QQ登陆.被迫用微信登陆后, 对手机号码和信息进行补全, 这样才可以用手机号码进行登录.

梨视频 ajax 请求视频资源 加上 URL 加密 限制爬虫

在视频的首页, 可以找到 某些视频的 url , 通过这个url , 就可以去到视频的详情页, 但是视频详情页里没有直接请求视频资源, 而是通过这个详情页面的 ajax 请求获取一个 json 数据, 这个 json 数据里有个字段返回了一个 假的 url, 通过对这个假的 url 替换部分字段后得到真的 url , 拿这个 真的 url 就可以获取到视频资源.浏览器抓包工具上已经拦截了浏览器发送的那个真的 url 请求, 我之所以 知道这个 反爬手段也是通过热心网友那里获知的, 他们手上应该有一个工具, 可以拿到被拦截的请求, 从而知道真实的url.
那个热心网友
由于他使用的是遍历字符串, 然后进行提取和拼接, 看得懂, 但是我写不出这么好的算法, 我就自己通过正则表达式提取然后拼接成真 url .下面是我自己的代码.

from lxml import etree
import requests
import re
import random
from multiprocessing.dummy import Pool
if __name__ == '__main__':

    headers = {
        "User-Agent": "Mozilla / 5.0(Windows NT 10.0;Win64;x64) AppleWebKit / 537.36(KHTML, likeGecko) Chrome / 87.0.4280.88Safari / 537.36"
    }

    # 拿到全部视频名字和 id
    def get_video_name_and_video_id(headers):
        url_lishipin_shenghuo = 'https://www.pearvideo.com/category_5'
        response_text = requests.get(url=url_lishipin_shenghuo, headers=headers).text
        tree = etree.HTML(response_text)
        top_list = tree.xpath('//*[@id="listvideoListUl"]/li[@class="categoryem "]')
        video_id = list()
        video_name = list()
        for i in top_list:
            video_id.append(i.xpath('./div/div/span/@data-id')[0])
            video_name.append(i.xpath('./div/a/div[2]/text()')[0] + ".mp4")
        return video_name, video_id

    #  拿到单个视频的假URL
    def get_false_url(id_one):
        ajax_url = 'https://www.pearvideo.com/videoStatus.jsp?'
        # print(ajax_url)
        params = {
            'contId': id_one,
            'mrd': str(random.random())  # 要传递一个随机数
        }
        ajax_headers = {
            "User-Agent": "Mozilla / 5.0(Windows NT 10.0;Win64;x64) AppleWebKit / 537.36(KHTML, likeGecko) Chrome / 87.0.4280.88Safari / 537.36",
            'Referer': 'https://www.pearvideo.com/video_' + str(id_one)
        }
        false_url = requests.get(url=ajax_url, headers=ajax_headers, params=params).json()
        false_url = false_url['videoInfo']['videos']['srcUrl']

        return false_url

    #  拿到全部 假的 url
    def get_all_false_urls(video_ids):
        list_urls = list()
        for i in range(len(video_ids)):
            flase_url = get_false_url(video_ids[i])
            list_urls.append(flase_url)
        return list_urls

    #  获得一条真的 url
    def get_true_url_one(false_url_one, id_one):
        header_url = 'https://video.pearvideo.com/mp4/'
        ex_2 = '/mp4/(.*)/2'
        dir_s = (re.findall(ex_2, false_url_one)[0])
        header_url += dir_s + '/'
        ex1 = "https://video.pearvideo.com/mp4/\D+/(.*)/"
        date_str = re.findall(ex1, false_url_one)[0]
        header_url += date_str
        ex = 'https://video.pearvideo.com/mp4/\S+/20\d+/(.*)'
        src = re.findall(ex, false_url_one)
        src = re.findall(ex, false_url_one)[0]
        data = (src.split("-"))
        ld_d = list()
        for j in range(len(data)):
            if j != 0:
                ld_d.append(data[j])

        end_url = header_url + "/cont-" + id_one
        for m in ld_d:
            end_url += "-" + m
        return end_url

    #  下载 mp4
    def loading_mp4(urls_one, video_names_one):
        mp4_source = requests.get(url=urls_one, headers=headers).content
        # print(mp4_source)
        with open(video_names_one, "wb") as f:
            f.write(mp4_source)
            # print("省略下载过程")
            # pass
            print("下载完成一个视频")

    # 下载单个视频
    def loading_one_video(dict_data):
        loading_mp4(dict_data['url'], dict_data['name'])

    # 拿到视频的名字, id
    video_names, video_ids = get_video_name_and_video_id(headers)

    # 拿到全部假的url
    false_urls = get_all_false_urls(video_ids)

    # 拿到全部的真url
    end_urls_true = list()
    for i in range(len(false_urls)):
        end_url = get_true_url_one(false_urls[i], video_ids[i])
        end_urls_true.append(end_url)

    # 把数据做成字典
    list_end = list()
    for d in range(len(video_names)):
        dict_data = dict()
        dict_data['name'] = video_names[d]
        dict_data['url'] = end_urls_true[d]
        list_end.append(dict_data)
    # 用线程池来处理下载任务
    pool = Pool(3)
    pool.map(loading_one_video, list_end)

12306 反爬

12306网站, 对于登陆频繁的IP, 验证码会加强, 比如普通用户是 给出一个提示, 找出符合条件的图片, 但是请求频繁的IP, 会要求给出两个条件, 找出符合这两个条件的图片, 然后还会有一个滑动到头的滑动条, 即使滚动条滑动成功了之后, 也会提示验证码有误,让你再次验证. 我发现了规律, 就是验证码前面两次, 就算你是肉眼观察后输入的, 肯定是正确的, 也会提示你错误, 让你输入三次验证码, 第三次正确才可以通过验证码验证.
同时, 在爬取12306的时候, 我们要隐藏自己是用 selenium的, 不然更加容易被封 ip .