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

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

为什么使用代理

开篇中我们爬来的代理ip怎么用?
(转)新手写爬虫v2.5(使用代理的异步爬虫)

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

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

使用代理

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

import requests

# 测试代理是否可用的URL,TEST_PROXY这个网站只返回访问者的ip
TEST_PROXY = 'http://icanhazip.com'

proxies = {
  "http": "http://10.10.1.10:3128",
  "https": "http://10.10.1.10:1080",
}

requests.get(TEST_PROXY, proxies=proxies)

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

class Proxy(object):
    """
    获取代理ips
    """
    def __init__(self, url, **kwargs):
        self.response = Spider(url, **kwargs).get()

    def test_proxy(self):
        """ 返回经测试可用的代理 """
        fail_num = 1
        success_num = 1
        success_proxy = []
        for ip_info in self.ips_info:
            proxy_str = ip_info['proxy_host']+':'+ip_info['proxy_port']
            proxies = dict(http='http://'+proxy_str)
            try:
                requests.get("http://icanhazip.com", timeout=5, proxies=proxies)
            except Exception:
                print '失败数:{}'.format(fail_num)
                fail_num += 1
                continue
            else:
                print '成功数:{}!'.format(success_num)
                success_num += 1
                success_proxy.append(ip_info)

        # 返回测试过,可用的代理
        print '结束:成功获取{}个代理'.format(len(success_proxy))
        return success_proxy

    @property
    def ips_info(self):
        """ 清理内容得到IP信息 """
        ips_list = []
        html_body = self.response.body
        soup = BeautifulSoup(html_body, "html.parser")
        ip_list_table = soup.find(id='ip_list')
        for fi_ip_info in ip_list_table.find_all('tr'):
            ip_detail = fi_ip_info.find_all('td')
            if ip_detail:
                # 注意:为什么我用list和str方法?否则就是bs4对象!!!
                ips_list.append(dict(proxy_host=str(list(ip_detail)[2].string),
                                     proxy_port=str(list(ip_detail)[3].string)))
        return ips_list

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

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

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

异步的代理爬虫

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

@gen.coroutine
def main():
    flag = 1
    ips_list = models.Proxy.find_all()
    for ip in ips_list:
        while 1:
            print 'proxy_ip {}:{}'.format(ip['proxy_host'], ip['proxy_port'])
            try:
                s = Spider(TEST, headers=CLIENT_CONFIG['headers'],
                           proxy_host=ip['proxy_host'], request_timeout=5,
                           proxy_port=int(ip['proxy_port']))

                response = yield s.async_get()
                print 'NO:{}: status {}'.format(flag, response.code)

            except HTTPError, e:
                print '换代理,错误信息:{}'.format(e)
                break
            else:
                flag += 1

if __name__ == '__main__':
    get_proxy_ips()
    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.

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

#!/usr/bin/env python
# -*- coding:utf-8 -*-
#
#   Author  :   XueWeiHan
#   E-mail  :   595666367@qq.com
#   Date    :   16/3/31 下午4:21
#   Desc    :   爬虫 v2.5
from bs4 import BeautifulSoup
from tornado.httpclient import HTTPRequest, HTTPClient, HTTPError
from tornado.curl_httpclient import CurlAsyncHTTPClient
from tornado import gen
from tornado.ioloop import IOLoop

try:
    from model import db
    from model import models
    from config import configs
    NO_DB = 1
    # 连接数据库
    db.create_engine(**configs['db'])
except ImportError:
    # NO_DB表示不用数据库
    NO_DB = 0
    print "Can't use db"
from client_config import CLIENT_CONFIG

# 测试用的访问目标(github API)
TEST = 'https://api.github.com/search/users?q=tom+repos:%3E42+followers:%3E1000'

# 测试代理是否可用的URL
TEST_PROXY = 'http://icanhazip.com'

# 获取代理的目标网站
URL = 'http://www.xicidaili.com/nn/'  # 高匿ip
#URL = 'http://www.xicidaili.com/nt/'  # 透明ip


class Spider(object):
    """

    """
    def __init__(self, url, **kwargs):
        self.request = HTTPRequest(url, **kwargs)

    @gen.coroutine
    def async_get(self, **kwargs):
        """ 异步get """
        ## 注意:只有CurlAsyncHTTPClient支持代理,所以这里用它
        response = yield CurlAsyncHTTPClient().fetch(self.request, **kwargs)
        raise gen.Return(response)

    def get(self, **kwargs):
        """ 同步get """
        return HTTPClient().fetch(self.request, **kwargs)

    def post(self):
        """ post暂时没用,先占坑 """
        self.request.method = "POST"
        return HTTPClient().fetch(self.request)


class Content(object):
    """
    存储(持久化)相关操作
    """
    def __init__(self, model=None):
        self.model = model

    def save(self, save_dict=None):
        """ 存到数据库 """
        if self.model:
            if save_dict:
                data 

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

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

为什么使用代理

开篇中我们爬来的代理ip怎么用?
(转)新手写爬虫v2.5(使用代理的异步爬虫)

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

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

使用代理

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

import requests

# 测试代理是否可用的URL,TEST_PROXY这个网站只返回访问者的ip
TEST_PROXY = 'http://icanhazip.com'

proxies = {
  "http": "http://10.10.1.10:3128",
  "https": "http://10.10.1.10:1080",
}

requests.get(TEST_PROXY, proxies=proxies)

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

class Proxy(object):
    """
    获取代理ips
    """
    def __init__(self, url, **kwargs):
        self.response = Spider(url, **kwargs).get()

    def test_proxy(self):
        """ 返回经测试可用的代理 """
        fail_num = 1
        success_num = 1
        success_proxy = []
        for ip_info in self.ips_info:
            proxy_str = ip_info['proxy_host']+':'+ip_info['proxy_port']
            proxies = dict(http='http://'+proxy_str)
            try:
                requests.get("http://icanhazip.com", timeout=5, proxies=proxies)
            except Exception:
                print '失败数:{}'.format(fail_num)
                fail_num += 1
                continue
            else:
                print '成功数:{}!'.format(success_num)
                success_num += 1
                success_proxy.append(ip_info)

        # 返回测试过,可用的代理
        print '结束:成功获取{}个代理'.format(len(success_proxy))
        return success_proxy

    @property
    def ips_info(self):
        """ 清理内容得到IP信息 """
        ips_list = []
        html_body = self.response.body
        soup = BeautifulSoup(html_body, "html.parser")
        ip_list_table = soup.find(id='ip_list')
        for fi_ip_info in ip_list_table.find_all('tr'):
            ip_detail = fi_ip_info.find_all('td')
            if ip_detail:
                # 注意:为什么我用list和str方法?否则就是bs4对象!!!
                ips_list.append(dict(proxy_host=str(list(ip_detail)[2].string),
                                     proxy_port=str(list(ip_detail)[3].string)))
        return ips_list

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

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

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

异步的代理爬虫

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

@gen.coroutine
def main():
    flag = 1
    ips_list = models.Proxy.find_all()
    for ip in ips_list:
        while 1:
            print 'proxy_ip {}:{}'.format(ip['proxy_host'], ip['proxy_port'])
            try:
                s = Spider(TEST, headers=CLIENT_CONFIG['headers'],
                           proxy_host=ip['proxy_host'], request_timeout=5,
                           proxy_port=int(ip['proxy_port']))

                response = yield s.async_get()
                print 'NO:{}: status {}'.format(flag, response.code)

            except HTTPError, e:
                print '换代理,错误信息:{}'.format(e)
                break
            else:
                flag += 1

if __name__ == '__main__':
    get_proxy_ips()
    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.

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

#!/usr/bin/env python
# -*- coding:utf-8 -*-
#
#   Author  :   XueWeiHan
#   E-mail  :   595666367@qq.com
#   Date    :   16/3/31 下午4:21
#   Desc    :   爬虫 v2.5
from bs4 import BeautifulSoup
from tornado.httpclient import HTTPRequest, HTTPClient, HTTPError
from tornado.curl_httpclient import CurlAsyncHTTPClient
from tornado import gen
from tornado.ioloop import IOLoop

try:
    from model import db
    from model import models
    from config import configs
    NO_DB = 1
    # 连接数据库
    db.create_engine(**configs['db'])
except ImportError:
    # NO_DB表示不用数据库
    NO_DB = 0
    print "Can't use db"
from client_config import CLIENT_CONFIG

# 测试用的访问目标(github API)
TEST = 'https://api.github.com/search/users?q=tom+repos:%3E42+followers:%3E1000'

# 测试代理是否可用的URL
TEST_PROXY = 'http://icanhazip.com'

# 获取代理的目标网站
URL = 'http://www.xicidaili.com/nn/'  # 高匿ip
#URL = 'http://www.xicidaili.com/nt/'  # 透明ip


class Spider(object):
    """

    """
    def __init__(self, url, **kwargs):
        self.request = HTTPRequest(url, **kwargs)

    @gen.coroutine
    def async_get(self, **kwargs):
        """ 异步get """
        ## 注意:只有CurlAsyncHTTPClient支持代理,所以这里用它
        response = yield CurlAsyncHTTPClient().fetch(self.request, **kwargs)
        raise gen.Return(response)

    def get(self, **kwargs):
        """ 同步get """
        return HTTPClient().fetch(self.request, **kwargs)

    def post(self):
        """ post暂时没用,先占坑 """
        self.request.method = "POST"
        return HTTPClient().fetch(self.request)


class Content(object):
    """
    存储(持久化)相关操作
    """
    def __init__(self, model=None):
        self.model = model

    def save(self, save_dict=None):
        """ 存到数据库 """
        if self.model:
            if save_dict:
                data 

相关文章:

  • 2021-11-04
  • 2022-12-23
  • 2022-12-23
  • 2021-12-28
  • 2021-11-23
  • 2021-11-04
  • 2021-11-04
  • 2022-12-23
猜你喜欢
  • 2021-11-04
  • 2021-12-09
  • 2021-11-04
  • 2022-12-23
  • 2021-11-04
  • 2021-12-11
相关资源
相似解决方案