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