用Python爬虫抓取免费代理IP

作者:HDMI,JUST WANT AND JUST DO
blog地址:zhihu.com/people/hdmi-b


不知道大家有没有遇到过“访问频率太高”这样的网站提示,我们需要等待一段时间或者输入一个验证码才能解封,但这样的情况之后还是会出现。出现这个现象的原因就是我们所要爬取的网页采取了反爬虫的措施,比如当某个ip单位时间请求网页次数过多时,服务器会拒绝服务,这种情况就是由于访问频率引起的封ip,这种情况靠解封不能很好的解决,所以我们就想到了伪装本机ip去请求网页,也就是我们今天要讲的使用代理ip。

目前网上有许多代理ip,有免费的也有付费的,例如西刺代理等,免费的虽然不用花钱但有效的代理很少且不稳定,付费的可能会好一点,不过今天我只爬取免费的代理并将检测是否可用,将可用ip存入MongoDB,方便下次取出。

运行平台:Windows

Python版本:Python3.6

IDE: Sublime Text

其他:Chrome浏览器

简述流程为:

步骤1:了解requests代理如何使用

步骤2:从代理网页爬取到ip和端口

步骤3:检测爬取到的ip是否可用

步骤4:将爬取的可用代理存入MongoDB

步骤5:从存入可用ip的数据库里随机抽取一个ip,测试成功后返回

对于requests来说,代理的设置比较简单,只需要传入proxies参数即可。

不过需要注意的是,这里我是在本机安装了抓包工具Fiddler,并用它在本地端口8888创建了一个HTTP代理服务(用Chrome插件SwitchyOmega),即代理服务为:127.0.0.1:8888,我们只要设置好这个代理,就可以成功将本机ip切换成代理软件连接的服务器ip了。

  1. import requests
  2. proxy ='127.0.0.1:8888'
  3. proxies ={
  4. 'http':'http://'+ proxy,
  5. 'https':'http://'+ proxy
  6. }
  7. try:
  8. response = requests.get('http://httpbin.org/get',proxies=proxies)
  9. print(response.text)
  10. except requests.exceptions.ConnectionErroras e:
  11. print('Error',e.args)

这里我是用来httpbin.org/get作为测试网站,我们访问该网页可以得到请求的有关信息,其中origin字段就是客户端ip,我们可以根据返回的结果判断代理是否成功。返回结果如下:

  1. {
  2. "args":{},
  3. "headers":{
  4. "Accept":"*/*",
  5. "Accept-Encoding":"gzip, deflate",
  6. "Connection":"close",
  7. "Host":"httpbin.org",
  8. "User-Agent":"python-requests/2.18.4"
  9. },
  10. "origin":"xx.xxx.xxx.xxx",
  11. "url":"http://httpbin.org/get"
  12. }

接下来我们便开始爬取代理IP,首先我们打开Chrome浏览器查看网页,并找到ip和端口元素的信息。

可以看到,代理IP以表格存储ip地址及其相关信息,所以我们用BeautifulSoup提取时很方便便能提取出相关信息,但是我们需要注意的是,爬取的ip很有可能出现重复的现象,尤其是我们同时爬取多个代理网页又存储到同一数组中时,所以我们可以使用集合来去除重复的ip。

将要爬取页数的ip爬取好后存入数组,然后再对其中的ip逐一测试。

这里就用到了上面提到的requests设置代理的方法,我们使用httpbin.org/ip作为测试网站,它可以直接返回我们的ip地址,测试通过后再存入MomgoDB数据库。

连接数据库然后指定数据库和集合,再将数据插入就OK了。

最后运行查看一下结果吧

运行了一段时间后,难得看到一连三个测试通过,赶紧截图保存一下,事实上是,毕竟是免费代理,有效的还是很少的,并且存活时间确实很短,不过,爬取的量大,还是能找到可用的,我们只是用作练习的话,还是勉强够用的。现在看看数据库里存储的吧。

因为爬取的页数不多,加上有效ip也少,再加上我没怎么爬,所以现在数据库里的ip并不多,不过也算是将这些ip给存了下来。现在就来看看怎么随机取出来吧。

由于担心放入数据库一段时间后ip会失效,所以取出前我重新进行了一次测试,如果成功再返回ip,不成功的话就直接将其移出数据库。

这样我们需要使用代理的时候,就能通过数据库随时取出来了。

总的代码如下:

  1. import random
  2. import requests
  3. import time
  4. import pymongo
  5. from bs4 importBeautifulSoup
  6. # 爬取代理的URL地址,选择的是西刺代理
  7. url_ip ="http://www.xicidaili.com/nt/"
  8. # 设定等待时间
  9. set_timeout =5
  10. # 爬取代理的页数,2表示爬取2页的ip地址
  11. num =2
  12. # 代理的使用次数
  13. count_time =5
  14. # 构造headers
  15. headers ={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.146 Safari/537.36'}
  16. # 测试ip的URL
  17. url_for_test ='http://httpbin.org/ip'
  18. def scrawl_xici_ip(num):
  19. '''
  20. 爬取代理ip地址
  21. '''
  22. ip_list =[]
  23. for num_page in range(1,num):
  24. url = url_ip + str(num_page)
  25. response = requests.get(url,headers=headers)
  26. if response.status_code ==200:
  27. content = response.text
  28. soup =BeautifulSoup(content,'lxml')
  29. trs = soup.find_all('tr')
  30. for i in range(1,len(trs)):
  31. tr = trs[i]
  32. tds = tr.find_all('td')
  33. ip_item = tds[1].text +':'+ tds[2].text
  34. # print(ip_item)
  35. ip_list.append(ip_item)
  36. ip_set =set(ip_list)# 去掉可能重复的ip
  37. ip_list = list(ip_set)
  38. time.sleep(count_time)# 等待5秒
  39. return ip_list
  40. def ip_test(url_for_test,ip_info):
  41. '''
  42. 测试爬取到的ip,测试成功则存入MongoDB
  43. '''
  44. for ip_for_test in ip_info:
  45. # 设置代理
  46. proxies ={
  47. 'http':'http://'+ ip_for_test,
  48. 'https':'http://'+ ip_for_test,
  49. }
  50. print(proxies)
  51. try:
  52. response = requests.get(url_for_test,headers=headers,proxies=proxies,timeout=10)
  53. if response.status_code ==200:
  54. ip ={'ip':ip_for_test}
  55. print(response.text)
  56. print('测试通过')
  57. write_to_MongoDB(ip)
  58. exceptExceptionas e:
  59. print(e)
  60. continue
  61. def write_to_MongoDB(proxies):
  62. '''
  63. 将测试通过的ip存入MongoDB
  64. '''
  65. client = pymongo.MongoClient(host='localhost',port=27017)
  66. db = client.PROXY
  67. collection = db.proxies
  68. result = collection.insert(proxies)
  69. print(result)
  70. print('存储MongoDB成功')
  71. def get_random_ip():
  72. '''
  73. 随机取出一个ip
  74. '''
  75. client = pymongo.MongoClient(host='localhost',port=27017)
  76. db = client.PROXY
  77. collection = db.proxies
  78. items = collection.find()
  79. length = items.count()
  80. ind = random.randint(0,length-1)
  81. useful_proxy = items[ind]['ip'].replace('\n','')
  82. proxy ={
  83. 'http':'http://'+ useful_proxy,
  84. 'https':'http://'+ useful_proxy,
  85. }
  86. response = requests.get(url_for_test,headers=headers,proxies=proxy,timeout=10)
  87. if response.status_code ==200:
  88. return useful_proxy
  89. else:
  90. print('此{ip}已失效'.format(useful_proxy))
  91. collection.remove(useful_proxy)
  92. print('已经从MongoDB移除')
  93. get_random_ip()
  94. def main():
  95. ip_info =[]
  96. ip_info = scrawl_xici_ip(2)
  97. sucess_proxy = ip_test(url_for_test,ip_info)
  98. finally_ip = get_random_ip()
  99. print('取出的ip为:'+ finally_ip)
  100. if __name__ =='__main__':
  101. main()