应对反爬虫问题(正在学习中)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Yk_0311/article/details/82703261

1.构造合理的HTTP请求头

目前我一般使用的是更改User-Agent
有些网站不喜欢爬虫访问,会检测连接对象,如果是爬虫程序不会让你访问

import requests
url='https://www.amazon.cn/'
hd={'User-Agent':'Mozilla/5.0'}
try:
    r=requests.get(url,headers=hd)
    r.raise_for_status()
    print(r.request.headers)
except:
    print('----')


#输出
{'User-Agent': 'Mozilla/5.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}

2.注意隐含输入字段

在 HTML 表单中,“隐含”字段可以让字段的值对浏览器可见,但是对用户不可见(除非看网页源代码)。

用隐含字段阻止网络数据采集的方式主要有两种。
第一种是表单页面上的一个字段可以用服务器生成的随机变量表示。如果提交时这个值不在表单处理页面上,服务器就有理由认为这个提交不是从原始表单页面上提交的,而是由一个网络机器人直接提交到表单处理页面的。绕开这个问题的最佳方法就是,首先采集表单所在页面上生成的随机变量,然后再提交到表单处理页面。

第二种方式是“蜜罐”(honey pot)。如果表单里包含一个具有普通名称的隐含字段(设置蜜罐圈套),比如“用户名”(username)或“邮箱地址”(email address),设计不太好的网络机器人往往不管这个字段是不是对用户可见,直接填写这个字段并向服务器提交,这样就会中服务器的蜜罐圈套。服务器会把所有隐含字段的真实值(或者与表单提交页面的默认值不同的值)都忽略,而且填写隐含字段的访问用户也可能被网站封杀。

那么该如何去避开蜜罐圈套

比如我们在进行网络爬虫的时候,我们很容易可以采集到信息,但是如果爬取到的一个网络表单的字段通过CSS设置成不可见,那么普通用户在浏览器上是看不的,所以也不可能填写这个字段,如果被被填写了,就会认为可能是爬虫干的,那么就是封禁你。

这种手段不仅可以应用在网站的表单上,还可以应用在链接、图片、文件,以及一些可以被机器人读取,但普通用户在浏览器上却看不到的任何内容上面。访问者如果访问了网站上的一个“隐含”内容,就会触发服务器脚本封杀这个用户的 IP 地址,把这个用户踢出网站,或者采取其他措施禁止这个用户接入网站。

介绍一个例子
http://pythonscraping.com/pages/itsatrap.html

from selenium import webdriver
url='http://pythonscraping.com/pages/itsatrap.html'
driver=webdriver.Chrome()
driver.get(url)
items=driver.find_elements_by_tag_name('a')
for item in items:
    if not item.is_displayed():#如果标签是不可见的
        print('{}链接不可见'.format(item.get_attribute('href')))
inputs=driver.find_elements_by_tag_name('input')
for item in inputs:
    if not item.is_displayed():
        print('{}此表单不可填写'.format(item.get_attribute('name')))



#输出
http://pythonscraping.com/dontgohere链接不可见
phone此表单不可填写
email此表单不可填写

3.创建自己的代理IP池

服务器会检测一个IP在单位时间内的请求次数,如果超过了某个阈值,那么服务器会直接拒绝服务,返回一些错误信息。
所以我们可以借助某种方式来伪装IP,让服务器无法识由我们本机发起的请求,这样子我们就可以防止封IP了

这里我们需要
1.去网站爬取免费的IP
2.检查爬去的IP是否有效
3.将有效的IP保存下来

那么怎么样才能检测一个IP是否有效呢?
第一种方式:利用requests.get(url,timeout=?),设定timeout一个值,超时了就说明不可用
第二种方式:用本机去ping这个IP,如果ping通,那么就说明这个IP也能去访问其他的网址

我利用第二种方式去实现
那么怎么利用python实现
Subprocess.Popen()可以创建一个进程,当shell参数为true时,程序通过shell来执行:

参数:
这里写图片描述

代码:

from bs4 import BeautifulSoup
import requests
import subprocess as sp
import re


def getIP(IPlist):  # 爬取网页上的免费ip,并且存入IPlist
    url = 'http://www.xicidaili.com/nn'  # 获得免费IP的网址
    hd = {'User-Agent': 'Mozilla/5.0'}  # 模拟浏览器进行访问
    try:
        r = requests.get(url, headers=hd)
        r.raise_for_status()
        r.encoding = r.apparent_encoding
        soup = BeautifulSoup(r.text, 'html.parser')
        # 根据页面结构来选择出元素
        list = (soup.select('tr')[1:])  # 用CSS选择器先找到tr标签,其中第一个标签是无效的
        for item in list:  # 遍历
            taglist = item.select('td')  # 找到td标签,其中第二,第三,第五个标签是我们想要的
            ip = taglist[5].string.lower() + '#' + taglist[1].string + '#' + taglist[2].string  # 添加#后面好处理
            IPlist.append(ip)
    except:
        print('1111')


def check_ip(ip):  # 检查ip是否可用
    cmd = 'ping -n 3 -w 3 {}'.format(ip)  # 本机能够ping通这个代理IP,那么我们也就可以使用这个代理IP去访问其他网站
    p = sp.Popen(cmd, stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE, shell=True)  # 使用Subprocess.Popen可以创建一个进程
    out = p.stdout.read().decode('gbk')
    # 丢包数
    Lost_Packets = loss(out)
    # 平均耗时
    time = average_time(out)
    # 选择出可用的ip,可用就返回True
    if Lost_Packets <= 1 and time <= 500:  # 当丢包数小于1,平均耗时小于500ms
        return True
    else:
        return False


def loss(out):  # 匹配出丢包数
    match = re.search(r'丢失 = (\d)', out)  # 利用正则表达式
    Lost_Packets = int(match.group(1))
    return Lost_Packets


def average_time(out):
    match2 = re.search(r'平均 = (\d*)ms', out)
    if match2:
        return int(match2.group(1))
    else:
        return 1000


def main():
    IPlist = []
    getIP(IPlist)
    ulist = []  # 用来存储可用的IP地址
    for IP in IPlist:  # 先检查前20个
        elements = IP.split('#')  # 以'#'分割,提取其中的关键信息
        ip = elements[1]
        # print(ip)
        if check_ip(ip):
            ulist.append({elements[0]: elements[1] + ':' + elements[2]})
    print(ulist)
main()


#给出部分输出
{'http': '111.194.14.157:8118'}, {'https': '106.14.47.5:80'}, {'http': '106.75.225.83:808'}, {'https': '218.59.228.18:61976'}

这样就好啦
以后学习到其他的继续更新

参考博文:https://blog.csdn.net/c406495762/article/details/72793480

猜你喜欢

转载自blog.csdn.net/Yk_0311/article/details/82703261