说实话,爬了什么多ip网站,这个网站的ip可用率是非常高的
但同时反爬虫机制也是稍微高端一点的
如果说用同一个IP进行频繁方位该网站会被Ban的
这些ip看似简单 就是一些数字
可能你认为直接爬取页面进行,re解析就可以
但是re完全行不通
但是你仔细分析分析一下你就会发现,卧槽 什么玩意 完全没有任何规律
不管是拆分也好长短也好,都是没有任何规律的
每一条数据由一个tr组成,tr的class分3种
每个tr里有8个td,td里面又分别记录着不同的div、span、p、a标记
只看一个就下定论的话,那就太没有说服力了
你会发现:排版没有任何规律可言
标签任意分布、
ip随机裁开、
通标签不同属性、
空标签、
每个ip的tr内的td的内容长短各不相同
去掉空标签,然后将每个div、span、p标签进行解析后
出来的数字、原点,将重复的去掉,然后在拼接回去。你会发现 竟然对了
欧耶 可以了!可能会有人说这还不简单吗,直接爬取网页,然后在进行re解析
将所有的数字解析
先来他一套正则表达式: .*?(\d*\\.*\d*).*?
将所有匹配结果去重、去空、然后在strip回去,大功告成
是的不错,这样确实可以匹配成功,但是成功率不超过百分之30
我觉得现在不是怎么去分析源码的问题了
应该是去分析人,去分析这个处理模板的人
比如说他会用什么样的方式来防御你,也就是换位思考
如果你是开发这个网站的人你会怎么样去反爬虫,处理模板时
即使你处理的再好也不要忘了,你最终还是要显示在网页上,最后还是会让用户去看的
不管你用多么高级算法去处理,最终还是要回到原点
那么我们就从原点出发,以作者的身份去分析
回到源码中,你就会发现原来一切是这么的简单
就好比之去重复的时候,你会发现如果出现重复数字,并且是不同标签
不管是re解析也好,xpath也好,还是Beautiful Soup,返回的list 都是每个标签的的内容占一个元素
如果返回字符串,拆分后 则无法进行去重
即使去重也是不合理的去重
就拿这个 1.10.186.228:61463
这个ip来说,当你去重的时候你会发现
不仅将重复的去掉了,但是 正确的也去掉了,因为有相邻的两个div的内容都是2
并且两个都是合法的,但是在list的他是两个元素,一旦去重 那么这个ip就废掉了
你会发现 这些内容不管以什么样的随机排列,都是这三个元素不会变,那文章肯定就出在元素上
那么我们就对这些重复内容进行分析,到底哪些是真正重复、哪些是迷惑的
将所有的标签统计处理,每一个 所有的p元素内容都出现过
将所有的p元素过滤掉然后再进行拼接和原ip一样
这样就达到了100%的匹配率
用xpath进行匹配,不仅不用去重而且通过节点的方式进行
进行匹配更准确
# xpath解析响应页面
lx = etree.HTML(html)
L1 = lx.xpath('//tbody//tr//td[contains(@class, "ip")]//span|//tbody//tr//td[contains(@class, "ip")]//div')
L2 = lx.xpath('//tbody//tr//td')
L3 = lx.xpath('//tbody//tr//a')
# 提取未解析ip
agency_list_all = self.parse_element(L1)
# 提取未解析详情
info_list_all = self.parse_element(L2)
# 提取未解析协议
deal_list_all = self.parse_element(L3)
self.parse_all(agency_list_all, info_list_all, deal_list_all)
xpath解析出来的列表中 存储了所有的分解ip **
有时7、8、9、12、13元素 组成一个ip
但是端口是默认80端口,或者全是4位及多位的端口
由于ip的点分十进制,最高255的性质,所以不可能出现4位的主机或服务器
利用端口进行分割,是80的或者是超过4位的都是端口
将前面的元素进行拼接得到的就是ip了,虽然有可能出现80,但是很少 我没遇到过
爬取成功率是99.99%
agency_list = []
info_list = []
deal_list = []
for _ in range(20):
for ip in agency_list_all:
if ip == "80" or len(ip) >= 4:
agency = agency_list_all[:agency_list_all.index(ip) + 1]
agency.insert(-1, ":")
agency = "".join(agency)
agency_list.append(agency)
del agency_list_all[:agency_list_all.index(ip) + 1]
break
别看这个网站只有20条ip,但是每一次刷新网页都会刷新ip
也就是刷新一次,会刷新20条免费ip,多爬几次ip就有了
我去什么鬼明明都匹配对了,但是 不能用是什么情况
当你拿到ip的时候你会发现,你用浏览器刷新拿到的完全不一样
浏览器刷新的可以用,爬到就不能用,难道还有更高深的反爬虫武功?
表面上看不出,实际上是一个他是一个js加载的动态页面
又不像其他的动态页面,他没有后续加载,是网页刷新后1秒内进行js加载
所以爬到的网页是没有被js过的源码,即使解析对了 但是得到的ip也是无效的
所以需要用selenium.webdirver
的方式进行抓取js后的页面
完整代码
import random
import time
import requests
import json
import linecache
import pymysql
import pymongo
import warnings
from selenium import webdriver
from lxml import etree
class MyUserAndIP(object):
def __init__(self):
self.base_url = "http://www.goubanjia.com/"
self.proxies = self.rand_ip()
self.headers = self.rand_user()
# Mongo
self.connect = pymongo.MongoClient("localhost", 27017)
self.db = self.connect.Myip
self.My_set = self.db.carwl_ip
# MySQL
self.DB = pymysql.connect("localhost", "root", "ParisPython", charset="UTF8Mb4")
self.cursor = self.DB.cursor()
warnings.filterwarnings("ignore")
try:
self.cursor.execute('create database if not exists Myip;')
self.cursor.execute('use Myip;')
self.cursor.execute('create table if not exists carwl_ip(id int primary key auto_increment,ip varchar(80))charset=utf8;')
except Warning:
pass
self.ip_list = []
# 爬取ip信息 1
def carwl_ip(self):
headers = {
"Host": "www.goubanjia.com",
"Connection": "keep-alive",
"Cache-Control": "max-age=0",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,zh;q=0.9",
"Cookie": "UM_distinctid=166ae0f9a7ed2b-0091945b9ddbf5-1f396652-1aeaa0-166ae0f9a7f163; JSESSIONID=B79E1850D3F8527268FA46346C931912; CNZZDATA1253707717=64889890-1540704336-%7C1540709760"
}
print("正在分析收集代理IP信息~")
# 响应对象
opt = webdriver.ChromeOptions()
opt.set_headless()
driver = webdriver.Chrome(options=opt)
driver.get(self.base_url)
time.sleep(1)
driver.execute_script('''
window.scrollTo(0, document.body.scrollHeight)
''')
time.sleep(5)
html = driver.page_source
driver.quit()
# 爬取的HTML 和 网页的源码不相同
# xpath解析响应页面
lx = etree.HTML(html)
L1 = lx.xpath('//tbody//tr//td[contains(@class, "ip")]//span|//tbody//tr//td[contains(@class, "ip")]//div')
L2 = lx.xpath('//tbody//tr//td')
L3 = lx.xpath('//tbody//tr//a')
# 提取未解析ip
agency_list_all = self.parse_element(L1)
# 提取未解析详情
info_list_all = self.parse_element(L2)
# 提取未解析协议
deal_list_all = self.parse_element(L3)
self.parse_all(agency_list_all, info_list_all, deal_list_all)
# 解析节点对象 2
def parse_element(self, lists):
lis = []
for i in lists:
if i.text:
lis.append(str(i.text).strip())
del lists
return lis
# 解析ip 详情 协议 3
def parse_all(self, agency_list_all, info_list_all, deal_list_all):
'''
:param agency_list_all:
匹配后 解析前 的代理ip端口
:param info_list_all:
匹配后 解析前 的详细信息
:param deal_list_all:
匹配后 解析前 的协议等信息
:return:
None
'''
agency_list = []
info_list = []
deal_list = []
for _ in range(20):
for ip in agency_list_all:
if ip == "80" or len(ip) >= 4:
agency = agency_list_all[:agency_list_all.index(ip) + 1]
agency.insert(-1, ":")
agency = "".join(agency)
agency_list.append(agency)
del agency_list_all[:agency_list_all.index(ip) + 1]
break
# 解析代理的详细信息
x = info_list_all[1:4]
tem = deal_list_all[0:4]
ip = agency_list[_]
ip = ip.split(":")
info = """
恭喜您~
IP: %s
端口: %s
代理地区: %s-%s 代理身份: %s
协议类型: %s
目前响应速度: %s
是否可用验证: %s
已经稳定使用: %s
详细信息:
http://www.ip138.com/ips138.asp?ip=%s&action=2
""" % (ip[0], ip[1], tem[2], tem[3], tem[0], tem[1], x[0], x[1], x[2], ip[0])
info_list.append(info)
# print(info)
del info_list_all[:4]
# 解析代理ip的协议
deal_list.append(tem[1])
del deal_list_all[:6]
del deal_list_all
del agency_list_all
del info_list_all
self.inspect_request(agency_list, info_list, deal_list)
# 检测ip 4
def inspect_request(self, agency_list, info_list, deal_list):
'''
检测爬取代理ip的可用性 正确性
:param agency_list:
代理ip列表 每个元素格式:
120.55.49.41:8498 | IP:d端口
:param info_list:
代理详细信息列表 元素格式:
详细信息字符串
:param deal_list:
代理IP协议类型
:return
None
'''
is_ip = []
for ip in agency_list:
idx = agency_list.index(ip)
# {'https': 'https://120.55.49.41:8498'}
dic = {
deal_list[idx]: deal_list[idx] + "://" + ip
}
try:
response = requests.get("http://www.baidu.com/",
proxies=dic,
headers={"User-Agent": "Mozilla/5.0"},
timeout=1)
if response.status_code == 200:
in_ip = ip.split(":")
if in_ip[0] not in is_ip:
is_ip.append(in_ip[0])
self.ip_list.append(dic)
print(info_list[idx])
except Exception:
continue
# on
def update_ip(self):
'''
将所有可用IP写入到本地文件
若想重新更新随机使用的代理ip请调用此方法
每次调用将随机爬取ip 并写入文件 方便下次读取
'''
try:
for i in range(20):
self.carwl_ip()
cmd = input('''
**********是否永久保存**********
默认:临时保存为ip.ini文件
每次更新ip.ini文件随之清空并更新
1.MongoDB 2.MySQL
3.保存为.txt 文件
(其他任意键代表默认)
*****************************
请选择:''')
cmd = cmd.strip()
if cmd == "1":
for ip in self.ip_list:
dic = {"ip": json.dumps(ip)}
self.My_set.insert(dic)
elif cmd == "2":
for ip in self.ip_list:
self.carwl_ip('insert into carwl_ip(ip) values(%s);' % (json.dumps(ip)))
self.DB.commit()
elif cmd == "3":
cmd = input("请输入文件名: ").strip()
if cmd[-4:] == ".txt":
cmd = cmd
else:
cmd += ".txt"
with open(cmd, "a", encoding="utf-8") as f:
for ip in self.ip_list:
f.write(json.dumps(ip) + "\n")
else:
with open("ip.ini", "w", encoding="utf-8") as f:
for ip in self.ip_list:
f.write(json.dumps(ip) + "\n")
except IndexError:
self.DB.close()
self.cursor.close()
finally:
# self.DB.close()
self.cursor.close()
# on
def rand_ip(self):
'''
每次随机读取ip.ini配置文件的一行内容 相当于每次随机换一次
爬取到的随机ip
:return:
返回一个字典格式如下 并且 可以直接放在requests中
作为proxies的参数
{'https': 'https://87.11.208.125:8587'}
'''
for _ in range(1, 20):
a = random.randint(0, 223)
theline = linecache.getline(r"ip.ini", a)
if not theline:
continue
# print(type(json.loads(theline)))
return json.loads(theline)
# on
def rand_user(self):
headers_list = [
{"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36"},
{"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60"},
{"User-Agent": "Opera/8.0 (Windows NT 5.1; U; en)"},
{"User-Agent": "Mozilla/5.0 (Windows NT 5.1; U; en; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.50"},
{"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0"},
{"User-Agent": "Mozilla/5.0 (X11; U; Linux x86_64; zh-CN; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10"},
{"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.57.2 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2"},
{"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36"},
{"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11"},
{"User-Agent": "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.133 Safari/534.16"},
{"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36"},
{"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"},
{"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.11 TaoBrowser/2.0 "},
{"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.71 Safari/537.1 LBBROWSER"},
{"User-Agent": "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; LBBROWSER)"},
{"User-Agent": "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.84 Safari/535.11 SE 2.X MetaSr 1.0"},
{"User-Agent": "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SV1; QQDownload 732; .NET4.0C; .NET4.0E; SE 2.X MetaSr 1.0)"},
{"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Maxthon/4.4.3.4000 Chrome/30.0.1599.101 Safari/537.36"},
{"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 UBrowser/4.0.3214.0 Safari/537.36"},
{"User-Agent": "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5"},
{"User-Agent": "Mozilla/5.0 (iPod; U; CPU iPhone OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5"},
{"User-Agent": "Mozilla/5.0 (iPad; U; CPU OS 4_2_1 like Mac OS X; zh-cn) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5"},
{"User-Agent": "Mozilla/5.0 (iPad; U; CPU OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5"},
{"User-Agent": "Mozilla/5.0 (Linux; U; Android 2.2.1; zh-cn; HTC_Wildfire_A3333 Build/FRG83D) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"},
{"User-Agent": "Mozilla/5.0 (Linux; U; Android 2.2.1; zh-cn; HTC_Wildfire_A3333 Build/FRG83D) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"},
{"User-Agent": "Opera/9.80 (Android 2.3.4; Linux; Opera Mobi/build-1107180945; U; en-GB) Presto/2.8.149 Version/11.10"},
{"User-Agent": "Mozilla/5.0 (Linux; U; Android 3.0; en-us; Xoom Build/HRI39) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13"},
{"User-Agent": "Mozilla/5.0 (BlackBerry; U; BlackBerry 9800; en) AppleWebKit/534.1+ (KHTML, like Gecko) Version/6.0.0.337 Mobile Safari/534.1+"},
{"User-Agent": "Mozilla/5.0 (hp-tablet; Linux; hpwOS/3.0.0; U; en-US) AppleWebKit/534.6 (KHTML, like Gecko) wOSBrowser/233.70 Safari/534.6 TouchPad/1.0"},
{"User-Agent": "Mozilla/5.0 (hp-tablet; Linux; hpwOS/3.0.0; U; en-US) AppleWebKit/534.6 (KHTML, like Gecko) wOSBrowser/233.70 Safari/534.6 TouchPad/1.0"},
{"User-Agent": "Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0; HTC; Titan)"},
{"User-Agent": "UCWEB7.0.2.37/28/999"},
{"User-Agent": "NOKIA5700/ UCWEB7.0.2.37/28/999"},
{"User-Agent": "Openwave/ UCWEB7.0.2.37/28/999"},
]
return random.choice(headers_list)
# 调试输出()
# req = MyUserAndIP()
# req.update_ip()
# for _ in range(1, 10):
# sip = req.rand_ip()
# ip = sip.get("http", "-1")
# if ip == "-1":
# ip = sip.get("https", "http://192.168.200.133:80")
# print(ip)
# chromeOptions = webdriver.ChromeOptions()
# # 设置代理
# chromeOptions.add_argument("--proxy-server=" + ip)
# # 一定要注意,=两边不能有空格,不能是这样--proxy-server = http://202.20.16.82:10152
# browser = webdriver.Chrome(chrome_options=chromeOptions)