开始

开篇:爬代理ip v2.0(未完待续),实现了获取代理ips,并把这些代理持久化(存在本地)。同时使用的是tornado的HTTPClient的库爬取内容。

中篇:开篇主要是获取代理ip;中篇打算使用代理ip,同时优化代码,并且异步爬取内容。所以接下来,就是写一个:异步,使用代理的爬虫。定义为:爬虫 v2.5

为什么使用代理

开篇中我们爬来的代理ip怎么用?

在需要发送请求的时候,需要把请求,先发送到代理服务器(通过代理ip和端口),再由代理服务器请求目标网站。目标网站返回响应的时候也是先返回给代理服务器。所以代理(代理服务器)做的事情就是转发请求,在转发请求的时候有三种方式,也就是分别对应着,透明代理、匿名代理、高匿名代理,解释请看开篇

所以,如果使用高匿名代理,就不会暴露真实的ip,目标网站只知道代理的ip。让爬虫循环使用把爬来的高匿名ip,从而降低,单一ip爬虫的被封ip和访问频率的问题。

使用代理

1.事情要一步步的做,首先我需要验证代理IP是否可用!最简单的方法就是用Request库,下面的例子,我就用Request官方文档的示例

  1. import requests
  2. # 测试代理是否可用的URL,TEST_PROXY这个网站只返回访问者的ip
  3. TEST_PROXY = 'http://icanhazip.com'
  4. proxies = {
  5. "http": "http://10.10.1.10:3128",
  6. "https": "http://10.10.1.10:1080",
  7. }
  8. requests.get(TEST_PROXY, proxies=proxies)

2.现在把爬取代理的方法和测试代理是否可用的方法,写成一个Proxy类。Proxy类做的事情就是‘返回经过测试的可用代理’,整理后代码如下:

  1. class Proxy(object):
  2. """
  3. 获取代理ips
  4. """
  5. def __init__(self, url, **kwargs):
  6. self.response = Spider(url, **kwargs).get()
  7. def test_proxy(self):
  8. """ 返回经测试可用的代理 """
  9. fail_num = 1
  10. success_num = 1
  11. success_proxy = []
  12. for ip_info in self.ips_info:
  13. proxy_str = ip_info['proxy_host']+':'+ip_info['proxy_port']
  14. proxies = dict(http='http://'+proxy_str)
  15. try:
  16. requests.get("http://icanhazip.com", timeout=5, proxies=proxies)
  17. except Exception:
  18. print '失败数:{}'.format(fail_num)
  19. fail_num += 1
  20. continue
  21. else:
  22. print '成功数:{}!'.format(success_num)
  23. success_num += 1
  24. success_proxy.append(ip_info)
  25. # 返回测试过,可用的代理
  26. print '结束:成功获取{}个代理'.format(len(success_proxy))
  27. return success_proxy
  28. @property
  29. def ips_info(self):
  30. """ 清理内容得到IP信息 """
  31. ips_list = []
  32. html_body = self.response.body
  33. soup = BeautifulSoup(html_body, "html.parser")
  34. ip_list_table = soup.find(id='ip_list')
  35. for fi_ip_info in ip_list_table.find_all('tr'):
  36. ip_detail = fi_ip_info.find_all('td')
  37. if ip_detail:
  38. # 注意:为什么我用list和str方法?否则就是bs4对象!!!
  39. ips_list.append(dict(proxy_host=str(list(ip_detail)[2].string),
  40. proxy_port=str(list(ip_detail)[3].string)))
  41. return ips_list

3.现在有Proxy类和Content类(开篇存储代理),下面写一个方法,把得到的,可用的代理ip,存到数据库中或许文本中。Content类,我修改一些地方,代码整理如下:

  1. def get_proxy_ips():
  2. """ 获取代理ips,并存储 """
  3. try:
  4. proxy = Proxy(url=URL, headers=CLIENT_CONFIG['headers'])
  5. ips_list = proxy.test_proxy()
  6. print ips_list
  7. except HTTPError as e:
  8. print '{}:Try again!!!'.format(e)
  9. get_proxy_ips()
  10. else:
  11. # 存到数据库中
  12. t = Content(models.Proxy)
  13. for ip_data in ips_list:
  14. t.save(ip_data)
  15. # 默认存到运行运行脚本的目录,文件名:data.txt
  16. t = Content()
  17. t.save_to_file(ips_list)

好了,下面就要开始写异步的代理爬虫了。

异步的代理爬虫

tornado的所有HTTPClient中,只有CurlAsyncHTTPClient支持代理。具体的实现逻辑请查看curl_httpclient源码,它也支持异步,所以接下来就是使用CurlAsyncHTTPClient的get方法,带着代理的host和port(注意:不需要指明协议)。代码如下:

  1. @gen.coroutine
  2. def main():
  3. flag = 1
  4. ips_list = models.Proxy.find_all()
  5. for ip in ips_list:
  6. while 1:
  7. print 'proxy_ip {}:{}'.format(ip['proxy_host'], ip['proxy_port'])
  8. try:
  9. s = Spider(TEST, headers=CLIENT_CONFIG['headers'],
  10. proxy_host=ip['proxy_host'], request_timeout=5,
  11. proxy_port=int(ip['proxy_port']))
  12. response = yield s.async_get()
  13. print 'NO:{}: status {}'.format(flag, response.code)
  14. except HTTPError, e:
  15. print '换代理,错误信息:{}'.format(e)
  16. break
  17. else:
  18. flag += 1
  19. if __name__ == '__main__':
  20. get_proxy_ips()
  21. IOLoop().run_sync(main)

优化改进

写完这个主爬虫,我思考了一下,那是否test_proxy方法是否也可以用异步呢?还有就是不应该使用requests库,因为tornado使用的PycURL库,同时我去看官方文档的时候关于速度的说明,PycURL文档

Speed - libcurl is very fast and PycURL, being a thin wrapper above libcurl, is very fast as well. PycURL was benchmarked to be several times faster than requests.

最后,支持直接用行脚本,兼容不使用数据库。全部代码如下:

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3. #
  4. # Author : XueWeiHan
  5. # E-mail : 595666367@qq.com
  6. # Date : 16/3/31 下午4:21
  7. # Desc : 爬虫 v2.5
  8. from bs4 import BeautifulSoup
  9. from tornado.httpclient import HTTPRequest, HTTPClient, HTTPError
  10. from tornado.curl_httpclient import CurlAsyncHTTPClient
  11. from tornado import gen
  12. from tornado.ioloop import IOLoop
  13. try:
  14. from model import db
  15. from model import models
  16. from config import configs
  17. NO_DB = 1
  18. # 连接数据库
  19. db.create_engine(**configs['db'])
  20. except ImportError:
  21. # NO_DB表示不用数据库
  22. NO_DB = 0
  23. print "Can't use db"
  24. from client_config import CLIENT_CONFIG
  25. # 测试用的访问目标(github API)
  26. TEST = 'https://api.github.com/search/users?q=tom+repos:%3E42+followers:%3E1000'
  27. # 测试代理是否可用的URL
  28. TEST_PROXY = 'http://icanhazip.com'
  29. # 获取代理的目标网站
  30. URL = 'http://www.xicidaili.com/nn/' # 高匿ip
  31. #URL = 'http://www.xicidaili.com/nt/' # 透明ip
  32. class Spider(object):
  33. """

  34. """
  35. def __init__(self, url, **kwargs):
  36. self.request = HTTPRequest(url, **kwargs)
  37. @gen.coroutine
  38. def async_get(self, **kwargs):
  39. """ 异步get """
  40. ## 注意:只有CurlAsyncHTTPClient支持代理,所以这里用它
  41. response = yield CurlAsyncHTTPClient().fetch(self.request, **kwargs)
  42. raise gen.Return(response)
  43. def get(self, **kwargs):
  44. """ 同步get """
  45. return HTTPClient().fetch(self.request, **kwargs)
  46. def post(self):
  47. """ post暂时没用,先占坑 """
  48. self.request.method = "POST"
  49. return HTTPClient().fetch(self.request)
  50. class Content(object):
  51. """
  52. 存储(持久化)相关操作
  53. """
  54. def __init__(self, model=None):
  55. self.model = model
  56. def save(self, save_dict=None):
  57. """ 存到数据库 """
  58. if self.model:
  59. if save_dict:
  60. data = self.model(**save_dict)
  61. data.insert()
  62. else:
  63. print 'no save_dict'
  64. else:
  65. print 'no model'
  66. @staticmethod
  67. def save_to_file(all_content, str_split=':', path='./data.txt'):
  68. """
  69. 把数据存到文件中
  70. :param all_content: 需要是list类型
  71. :param str_split: 分割符号
  72. :param path: 文件位置,默认为当前脚本运行的位置,文件名:data.txt
  73. """
  74. with open(path, 'w') as fb:
  75. print '开始写入文件'
  76. for content in all_content:
  77. content_str = ''
  78. for k, v in content.items():
  79. content_str += v + str_split
  80. fb.write(content_str+'\n')
  81. print '写入文件完成'
  82. class Proxy(object):
  83. """
  84. 获取代理ips
  85. """
  86. def __init__(self, url, **kwargs):
  87. self.response = Spider(url, **kwargs).get()
  88. @gen.coroutine
  89. def test_proxy(self):
  90. """ 返回经测试可用的代理 """
  91. # flag用于计数
  92. flag = 1
  93. all_ips = self.ips_info()
  94. print '初始化爬到{}个代理,下面开始测试这些代理的可用性:'.format(len(all_ips))
  95. success_proxy = []
  96. for ip_info in all_ips:
  97. try:
  98. s = Spider(TEST_PROXY, headers=CLIENT_CONFIG['headers'],
  99. proxy_host=ip_info['proxy_host'], request_timeout=5,
  100. proxy_port=int(ip_info['proxy_port']))
  101. yield s.async_get()
  102. except Exception:
  103. print '第{}个,失败。'.format(flag)
  104. continue
  105. else:
  106. print '第{}个:成功!'.format(flag)
  107. success_proxy.append(ip_info)
  108. finally:
  109. flag += 1
  110. # 返回测试过,可用的代理
  111. print '经测试:{}个可用,可用率:{}%'.format(len(success_proxy),
  112. len(success_proxy)/len(all_ips))
  113. raise gen.Return(success_proxy)
  114. def ips_info(self):
  115. """ 清理内容得到IP信息 """
  116. ips_list = []
  117. html_body = self.response.body
  118. soup = BeautifulSoup(html_body, "html.parser")
  119. ip_list_table = soup.find(id='ip_list')
  120. for fi_ip_info in ip_list_table.find_all('tr'):
  121. ip_detail = fi_ip_info.find_all('td')
  122. if ip_detail:
  123. # 注意:为什么我用list和str方法?否则就是bs4对象!!!
  124. ips_list.append(dict(proxy_host=str(list(ip_detail)[2].string),
  125. proxy_port=str(list(ip_detail)[3].string)))
  126. return ips_list
  127. @gen.coroutine
  128. def get_proxy_ips():
  129. """ 获取代理ips,并存储 """
  130. try:
  131. proxy = Proxy(url=URL, headers=CLIENT_CONFIG['headers'])
  132. ips_list = yield proxy.test_proxy()
  133. except HTTPError as e:
  134. print 'Try again! Error info:{}'.format(e)
  135. else:
  136. if NO_DB:
  137. # 存到数据库中
  138. t = Content(models.Proxy)
  139. for ip_data in ips_list:
  140. t.save(ip_data)
  141. # 默认存到运行运行脚本的目录,文件名:data.txt
  142. t = Content()
  143. t.save_to_file(ips_list)
  144. raise gen.Return(ips_list)
  145. @gen.coroutine
  146. defmain():
  147. """ 使用代理的异步爬虫 """
  148. flag = 1
  149. ips_list = yield get_proxy_ips()
  150. for ip in ips_list:
  151. while 1:
  152. print 'Use proxy ip {}:{}'.format(ip['proxy_host'], ip['proxy_port'])
  153. try:
  154. # 这里就是异步的代理爬虫,利用代理获取目标网站的信息
  155. s = Spider(TEST, headers=CLIENT_CONFIG['headers'],
  156. proxy_host=ip['proxy_host'], request_timeout=10,
  157. proxy_port=int(ip['proxy_port']))
  158. # response爬虫返回的response对象,response.body就是内容
  159. response = yield s.async_get()
  160. print 'NO:{}: status {}'.format(flag, response.code)
  161. except HTTPError, e:
  162. print '换代理,错误信息:{}'.format(e)
  163. break
  164. else:
  165. flag += 1
  166. if __name__ == '__main__':
  167. IOLoop().run_sync(main)

运行效果:

TODO

  1. 这个脚本可用性还是很差,因为爬的是免费代理,代理的稳定性很差,很差,很差!!!会导致,测试可用的代理。但是在使用的时候就连不上了!所以,后面想学习怎么自己搞到代理。我发现使用代理的爬虫,代理的稳定性,至关重要!应该会用到的技术:扫端口?计算机网络的知识。

  2. 现在的流程是:爬取并测试所有的代理,然后持久化可用的代理。后面想做成,如果有成功的代理,后面的爬虫就工作,使用成功的代理爬取目标URL。应该会用到的技术:队列,多线程

  3. 速度还是很慢。

(转)新手写爬虫v2.5(使用代理的异步爬虫)的更多相关文章

  1. [python]新手写爬虫v2.5(使用代理的异步爬虫)

    开始 开篇:爬代理ip v2.0(未完待续),实现了获取代理ips,并把这些代理持久化(存在本地).同时使用的是tornado的HTTPClient的库爬取内容. 中篇:开篇主要是获取代理ip:中篇打 ...

  2. 深入理解协程(四):async/await异步爬虫实战

    本文目录: 同步方式爬取博客标题 async/await异步爬取博客标题 本片为深入理解协程系列文章的补充. 你将会在从本文中了解到:async/await如何运用的实际的爬虫中. 案例 从CSDN上 ...

  3. [Python]新手写爬虫全过程(转)

    今天早上起来,第一件事情就是理一理今天该做的事情,瞬间get到任务,写一个只用python字符串内建函数的爬虫,定义为v1.0,开发中的版本号定义为v0.x.数据存放?这个是一个练手的玩具,就写在tx ...

  4. [Python]新手写爬虫全过程(已完成)

    今天早上起来,第一件事情就是理一理今天该做的事情,瞬间get到任务,写一个只用python字符串内建函数的爬虫,定义为v1.0,开发中的版本号定义为v0.x.数据存放?这个是一个练手的玩具,就写在tx ...

  5. (转)Python新手写出漂亮的爬虫代码2——从json获取信息

    https://blog.csdn.net/weixin_36604953/article/details/78592943 Python新手写出漂亮的爬虫代码2——从json获取信息好久没有写关于爬 ...

  6. (转)Python新手写出漂亮的爬虫代码1——从html获取信息

    https://blog.csdn.net/weixin_36604953/article/details/78156605 Python新手写出漂亮的爬虫代码1初到大数据学习圈子的同学可能对爬虫都有 ...

  7. Python爬虫02——贴吧图片爬虫V2.0

    Python小爬虫——贴吧图片爬虫V2.0 贴吧图片爬虫进阶:在上次的第一个小爬虫过后,用了几次发现每爬一个帖子,都要自己手动输入帖子链接,WTF这程序简直反人类!不行了不行了得改进改进. 思路: 贴 ...

  8. 反爬虫之搭建IP代理池

    反爬虫之搭建IP代理池 听说你又被封 ip 了,你要学会伪装好自己,这次说说伪装你的头部.可惜加了header请求头,加了cookie 还是被限制爬取了.这时就得祭出IP代理池!!! 下面就是requ ...

  9. python scrapy版 极客学院爬虫V2

    python scrapy版 极客学院爬虫V2 1 基本技术 使用scrapy 2 这个爬虫的难点是 Request中的headers和cookies 尝试过好多次才成功(模拟登录),否则只能抓免费课 ...

随机推荐

  1. nodejs之url模块

    鄙人初步学习nodejs,目前在读<nodejs入门>这一本书,书很小,但是让我知道了如何用nodejs创建一个简单的小项目.例如如何创建一个服务器啦,例如http.createServe ...

  2. nopCommerce 3.9 大波浪系列 之 NUnit 配置调试环境

    官网:http://nunit.org/ GitHub:https://github.com/nunit 本文只介绍NUnit在VS中的配置使用,进一步学习 NUnit 请参考官方文档. nopCom ...

  3. USB的四种传输类型与端点

    1.事务 在介绍USB传输类型之前,请允许我先简答介绍一下USB事务. 事务一般由令牌包.数据包(可选).握手包组成. 令牌包:用来启动一个事务,总是由主机发送. 数据包:可以从主机到设备,也可以由设 ...

  4. pug模板引擎(原jade)

    前面的话 为什么要引入pug,pug有什么特别之处呢?有一些嵌套层次较深的页面,可能会出现巢状嵌套,如下图所示 在后期维护和修改时,一不小心少了一个尖括号,或者某个标签的开始和闭合没有对应上,就会导致 ...

  5. 支持多个版本的ASP.NET Core Web API

    基本配置及说明 版本控制有助于及时推出功能,而不会破坏现有系统. 它还可以帮助为选定的客户提供额外的功能. API版本可以通过不同的方式完成,例如在URL中添加版本或通过自定义标头和通过Accept- ...

  6. DNS,TCP,IP,HTTP,socket,Servlet概念整理

    DNS,TCP,IP,HTTP,socket,Servlet概念整理   常见的协议虽然很容易理解,但是看了之后过一段时间不看还是容易忘,笔记如下,比较零碎,勉强供各位复习.如有错误欢迎指正.   D ...

  7. 【YII】Yii入门

    1. 入门博客 http://blog.csdn.net/zm2714/article/category/1359776/2 2. 创建运行demo http://blog.csdn.net/zhou ...

  8. create pfile from spfile;

    sql>create pfile from spfile; 生成的文件在$ORACLE_HOME/dbs/下边    和spfile在同一个目录下 但是名字已经变成了init$oracle_si ...

  9. Redux源码分析之bindActionCreators

    Redux源码分析之基本概念 Redux源码分析之createStore Redux源码分析之bindActionCreators Redux源码分析之combineReducers Redux源码分 ...

  10. Warning[w2]: Symbol ?P…

    屏蔽的方法: 一.找到$PROJ_DIR$\..\..\..\Tools\CC2530DB\f8w2530.xcl位置 二.找到f8w2530.xcl 三.打开f8w2530.xcl,注释D?PBAN ...