背景
很多时候我们会使用爬虫或者脚本帮我们爬取数据用于数据分析、模型训练等,常见的反爬手段之一就是限制IP的访问频率,所以为了让爬虫或脚本能够正常运行,我们需要有足够多的IP。代理IP就是通过代理的IP去访问我们需要的网站,对于目标网站表现出的是代理IP, 从而防止同一IP访问频率过高被封。如何拥有足够多的代理地址呢?我们可以使用爬虫手段去自动爬取提供免费代理的网站,建立自己的代理池。本篇以爬取66代理为例,记录整个过程。
代理网站分析
打开网址后,页面如下:
为了快速找到我们需要的信息所在位置,我们使用xpath工具,可以在浏览器的插件市场搜索安装(XPath Helper)。
shift+鼠标左键点击表格:
可以看到我们需要的信息位于一个html表格中,因此我们只需要找出这个表格,然后遍历表格就可以获取到我们需要的信息。
另一方面,我们点击页面下方的翻页时,会发现网页地址是http://www.xxx.cn/2.html这种形式,这样翻页也就解决了。
实现
为了方便扩展到其他代理网站,我们先建立一个代理基类:
class BaseProxy:
"""
代理基类
"""
def __init__(self, page):
self._proxies = self._get_proxies()
self._page = page
def _get_proxies():
return []
def get_one(self):
"""
随机返回一个代理
:return:
"""
if not self._proxies:
return []
return random.choice(self._proxies)
def get_all(self):
"""
返回所有代理
:return:
"""
return self._proxies
def check_proxy(self, proxy):
"""
检测代理是否可用
:return:
"""
url = 'https://api.ipify.org/?format=json'
try:
res = requests.get(url, proxies=proxy, timeout=3).json()
if 'ip' in res:
return True
except Exception as e:
return False
下面写一个专属于66代理类,主要实现一个获取代理地址的接口:
class SixSixProxy(BaseProxy):
"""
66代理
"""
def __init__(self, page=10):
super().__init__(page)
self._count = page * 10
self.base_url = "http://www.66ip.cn/{}.html"
self._proxies = self._get_proxies()
def _get_proxies(self):
proxies = []
for page in range(self._page):
if page == 0:
url = "http://www.66ip.cn/index.html"
else:
url = self.base_url.format(page+1)
try:
# 创建浏览器对象
options = webdriver.EdgeOptions()
options.add_argument('--headless')
driver = webdriver.Edge(options=options)
# 加载页面
driver.get(url)
# 执行JavaScript代码获取完整的HTML内容
html = driver.execute_script('return document.documentElement.outerHTML')
# 关闭浏览器
driver.quit()
selector = etree.HTML(html)
table = selector.xpath("/html/body/div[@id='main']/div[@class='containerbox boxindex']/div[@class='layui-row layui-col-space15']/div[1]/table/tbody/tr")
for tr in table[1:]:
ip = tr.xpath("./td[1]/text()")[0]
port = tr.xpath("./td[2]/text()")[0]
proxy_type = tr.xpath("./td[4]/text()")[0]
if proxy_type !="高匿代理":
continue
protocol = "http"
proxy = {
"http": '{}://{}:{}'.format(protocol, ip, port)
}
proxies.append(proxy)
except Exception as e:
print(e)
return proxies
为了防止页面中部分信息是通过js获取而不完整,我们直接使用了selenium, 通过webdriver获取完整的html中,通过xpath获取我们需要的表格,然后遍历表格即可得到表格中的ip地址和端口。
测试
Proxy = SixSixProxy()
print(Proxy.get_all())