教你一种1分钟,下载1万个网页的方法,你学吗?

数据采集 专栏收录该内容
13 篇文章 0 订阅

一:模块介绍

Pycurl是一个用C语言编写的libcurl Python实现,功能非常强大,支持操作协议有FTP,HTTP,HTTPS,TELNET等。urllib相比,Pycurl的速度要很多。

二:安装

大家可以去官网下载与本地Python一直的whl或exe包。

 

也可以使用下面的命令行直接安装。

 

pip install pycurl

主要方法

 

 

四:示例DEMO

下面的示例代码是我2015年开发通用爬虫时,使用的一部分,现在已经改成并发模式了(后面会有简单的介绍),不过大家还是可以做个参考。

# encoding=utf-8

import pycurl , traceback

from com.fy.utils.html.HtmlCode import HtmlCodeUtils

from io import BytesIO  # <-- 这个用到里面的write函数

class Utils_PyCurl:

    def __init__(self,):

        self.hcu = HtmlCodeUtils() #根据HTML源码获取网页编码

        self.c = pycurl.Curl()

        self.c.setopt(pycurl.CONNECTTIMEOUT, 60)#链接超时

        self.c.setopt(pycurl.TIMEOUT, 60) #下载超时

        self.c.setopt(pycurl.MAXREDIRS, 5)#设置重定向次数

        self.c.setopt(pycurl.USERAGENT, 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)')#模拟浏览器

        

    def PyCurl_Html(self, url):

        self.requestCode = 0#URL请求返回码

        host = url

        for i in range(2):

            del  i

            html, code = self.fetch_html_pycurl(host)

            if int(self.requestCode) == 200:

                return  html, code

            elif html == None:

                continue

        return None, None

    

    def fetch_html_pycurl(self, host):

        result = None

        try:

            self.c.setopt(self.c.URL, host)

            self.c.setopt(self.c.HTTPHEADER, ['Accept:'])

            e = BytesIO()

            self.c.setopt(self.c.WRITEFUNCTION, e.write)

            self.c.setopt(self.c.FOLLOWLOCATION, 1)

            self.c.perform()

            data = e.getvalue();

            html_code = self.hcu.getChardet(data)#获取网页编码

            result = str(data.decode(html_code, 'ignore'))

            self.requestCode = self.c.getinfo(self.c.HTTP_CODE)

            if result != None:print(len(result), self.requestCode)

            else:print(self.requestCode)

        except :print(traceback.print_exc())

        return result, self.requestCode

pyc = Utils_PyCurl()

html, code = pyc.PyCurl_Html("文章地址")

print("code:", code)

print(html)

五:并发

大批量数据采集时,我们需要对上述的代码进行二次封装,实现并发处理,提高下载的速度。多线程是实现并发最基础的方式,这里就不再次介绍。今天我们介绍一下基gevent协程的并发处理。

1:Gevent介绍

gevent是Python世界中最重要的异步网络库,可以大幅度提高系统的性能。最可贵的是,它允许我们几乎不修改代码,把同步程序变为异步程序。

2016年是Python3的重要时刻。Scrapy刚宣布支持Python3不久,Gevent又宣布支持Python3了。

给大家一个编码建议:如果用Python2,只用Python2.7;如果使用Python3,请至少使用Python3.4,最好使用Python3.5

支持Python版本:

Python2.7及Python>=3.4

Gevent是基于协程的Python网络库。

包含的特性:

• 基于libev的快速事件循环(Linux上epoll,FreeBSD上kqueue)。


• 基于greenlet的轻量级执行单元。


•API的概念和Python标准库一致(如事件,队列)。


•可以配合socket,ssl模块使用。


•能够使用标准库和第三方模块创建标准的阻塞套接字(gevent.monkey)。


•默认通过线程池进行DNS查询,也可通过c-are(通过GEVENT_RESOLVER=ares环境变量开启)。


•TCP/UDP/HTTP服务器


•子进程支持(通过gevent.subprocess)


•线程池

 

 2:安装

​​​​​​​pip install wheel

pip install gevent 或者 pip3 install gevent

具体安装命令视安装的Python 的版本而定。当出现 Successfully installed ...... 的提示后,即表示 gevent 安装成功。

 

如果中途出现异常,一定是依赖的包没有提前安装。具体的问题,根据提示处理,基本上就可以解决了。

3:基于的Geventpycurl 并发模式改造后的完整代码如下:


# encoding=utf-8
import pycurl , time, traceback
#如果没有给gevent打上补丁的话,它是检测不到除gevent它本省自带的IO操作的,当打上了补丁,它就能检测到程序其他所有的IO操作
from com.fy.utils.html.HtmlCode import HtmlCodeUtils
from io import BytesIO  # <-- 这个用到里面的write函数
from gevent import monkey; 
monkey.patch_all()#把当前程序的所有IO操作给我单独的做上标记
#如果没有给gevent打上补丁的话,它是检测不到除gevent它本省自带的IO操作的,当打上了补丁,它就能检测到程序其他所有的IO操作
import  gevent  
class Utils_PyCurl:
    def __init__(self,):
        self.hcu = HtmlCodeUtils() #根据HTML源码获取网页编码
        self.c = pycurl.Curl() 
        self.c.setopt(pycurl.CONNECTTIMEOUT, 60)#链接超时
        self.c.setopt(pycurl.TIMEOUT, 60) #下载超时
        self.c.setopt(pycurl.MAXREDIRS, 5)#设置重定向次数
        self.c.setopt(pycurl.USERAGENT, 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)')#模拟浏览器

    def geventControl(self, tasks, urlFieldName): 
        print("待处理的任务【" + str(len(tasks)) + "】个...")
        start_time = time.time()
        self.urlFieldName = urlFieldName#集合中存放地址的key
        jobs = [gevent.spawn(self.fetch_html_pycurl, task) for task in tasks]
        htmls = gevent.joinall(jobs, timeout=60, raise_error=False)
        end_time = time.time()
        print("下载页面【完毕】,共历时:【" + str((end_time - start_time))[0:4] + "】s.....\n")
        del htmls
        return tasks, (end_time - start_time)
        
    def fetch_html_pycurl(self, task):
        host = task[self.urlFieldName]#待处理的地址
        html = None#HTML源码
        requestCode = 200#返回的状态码
        try:
            self.c.setopt(self.c.URL, host) 
            self.c.setopt(self.c.HTTPHEADER, ['Accept:'])
            e = BytesIO()
            self.c.setopt(self.c.WRITEFUNCTION, e.write)
            self.c.setopt(self.c.FOLLOWLOCATION, 1) 
            self.c.perform()
            data = e.getvalue();
            html_code = self.hcu.getChardet(data)#获取网页编码
            html = str(data.decode(html_code, 'ignore'))
            requestCode = self.c.getinfo(self.c.HTTP_CODE)
            if html != None:print("HTML-length:", len(html), "HTTP_CODE:", requestCode, "request-url:", host)
            else:print("HTML-length:", 0, "HTTP_CODE:", requestCode, "request-url:", host)
        except :
            requestCode = 400
            print(traceback.print_exc())
        task['HTML'] = html#HTML源码
        task['HTTP_CODE'] = requestCode#返回的状态码
        return task
pyc = Utils_PyCurl()
start_time = time.time()
for pageNo in range(1, 11):
    tasks = []
    for i in  range(1, 100):
        task = {}
        task['url'] = "文章地址"
        tasks.append(task) 
    pyc.geventControl(tasks, "url")  
end_time = time.time()
print((end_time - start_time))

通过上述改造完以后,对其做了个简单的测试。

样本URL:1000个,分十批,每批次100个URL。共历时49秒

用的是我的一个用了三年的笔记本,性能较差,如果是放在服务器的Linux环境下的话,30秒内下载完毕,绝对不在话下。

在分布式大批量采集中,使用多进程+协程并发的模式,单台服务器,一分钟下载1W个网页,绝对没有问题。

今天就说这么多了,改天介绍一下采集的整体架构

  • 0
    点赞
  • 1
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2020 CSDN 皮肤主题: 我行我“速” 设计师:Amelia_0503 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值