Python爬虫基础操作三

目录

八、协程

1、多协程的用法gevent

2、queue模块

九、Scrapy框架

1、Scrapy的结构

2、Scrapy安装

3、Scrapy使用流程

十、爬虫进阶方向与反爬虫

1、爬虫进阶方向

2、反爬虫


八、协程

前几章讲了python爬虫基础操作步骤:

  • 获取数据
  • 解析数据
  • 提取数据
  • 存储数据
  • 定时邮件

前几章爬取的网页所涉及的数据量不大,但是当我们要爬取成千上万的数据时,因为要一行行一次执行,我们要等很久才能拿到想要的数据。那么我们就需要让多个爬虫一起爬取,并发执行提高爬取的效率。它的原理是:一个任务在执行的过程中,如果遇到等待,先去执行其他的任务,当等待结束,再回来继续执行之前的任务,在计算机的世界,这种任务来回切换得非常快速,看上去就像多个任务被同时执行一样。这种方式就叫多协程,是一种非抢占的异步技术。使用多协程,就能让多个爬取任务用异步的方式交替执行。

1、多协程的用法gevent

在python中,可以使用gevent库实现多协程。这是一个轻量级协同程序。

gevent库安装: windows环境:pip install gevent

gevent实现多协程爬取的步骤:

  • 定义爬取函数
  • 用gevent.spawn()创建任务
  • 用gevent.joinall()执行任务

参考资料:

gevent官方文档:http://www.gevent.org/contents.html

gevent中文参考文档:https://github.com/panguangyu/gevent-tutorial-chinese

gevent使用示例:

# Description:使用多协程爬取8个网页

from gevent import monkey
#从gevent库里导入monkey模块。
monkey.patch_all()
#monkey.patch_all()能把程序变成协作式运行,就是可以帮助程序实现异步。
import gevent,time,requests
#导入gevent、time、requests。

start = time.time()
#记录程序开始时间。

url_list = ['https://www.baidu.com/',
'https://www.sina.com.cn/',
'http://www.sohu.com/',
'https://www.qq.com/',
'https://www.163.com/',
'http://www.iqiyi.com/',
'https://www.tmall.com/',
'http://www.ifeng.com/']
#把8个网站封装成列表。

def crawler(url):
#定义一个crawler()函数。
    r = requests.get(url)
    #用requests.get()函数爬取网站。
    print(url,time.time()-start,r.status_code)
    #打印网址、请求运行时间、状态码。

tasks_list = [ ]
#创建空的任务列表。

for url in url_list:
#遍历url_list。
    task = gevent.spawn(crawler,url)
    #用gevent.spawn()函数创建任务。
    tasks_list.append(task)
    #往任务列表添加任务。
gevent.joinall(tasks_list)
#执行任务列表里的所有任务,就是让爬虫开始爬取网站。
end = time.time()
#记录程序结束时间。
print(end-start)
#打印程序最终所需时间。

第1、3行代码:从gevent库里导入了monkey模块,这个模块能将程序转换成可异步的程序。monkey.patch_all(),它的作用其实就像你的电脑有时会弹出“是否要用补丁修补漏洞或更新”一样。它能给程序打上补丁,让程序变成是异步模式,而不是同步模式。它也叫“猴子补丁”。我们要在导入其他库和模块前,先把monkey模块导入进来,并运行monkey.patch_all()。这样,才能先给程序打上补丁。

第21、23、25行代码:我们定义了一个crawler函数,只要调用这个函数,它就会执行【用requests.get()爬取网站】和【打印网址、请求运行时间、状态码】这两个任务

第33行代码:因为gevent只能处理gevent的任务对象,不能直接调用普通函数,所以需要借助gevent.spawn()来创建任务对象。gevent.spawn()的参数需为要调用的函数名及该函数的参数。比如,gevent.spawn(crawler,url)就是创建一个执行crawler函数的任务,参数为crawler函数名和它自身的参数url。

第37行代码:调用gevent库里的joinall方法,能启动执行所有的任务。gevent.joinall(tasks_list)就是执行tasks_list这个任务列表里的所有任务,开始爬取。

 

2、queue模块

上面的示例中我们用多协程爬取的是8个网站,如果要爬取更多的网站,创建大量的任务时,我们可以借助queue模块。

queue就是队列,我们可以用queue模块来存储任务,让任务变成一条整机的队列,就像银行窗口排队的做法。其实queue就是一种有序的数据结构,可以用来存取数据。

这样,协程就可以从队列里把任务提取出来执行,知道队列孔了,任务也就处理完了。

queue对象的方法
put_nowait()往队列里存储数据
get_nowait()从队列里提取数据
empty()判断队列是否为空
full()

判断队列是否为满

qsize()判断队列还剩多少数量

用queue模块的过程:

  • 用Queue()创建队列
  • 用put_nowait()存储数据
  • 用get_nowait()提取数据
  • 用empty()为判断依据循环执行任务

queue使用示例:

# Description:多协程,使用gevent,queue模块爬取网址数据

from gevent import monkey
#从gevent库里导入monkey模块。
monkey.patch_all()
#monkey.patch_all()能把程序变成协作式运行,就是可以帮助程序实现异步。
import gevent,time,requests
#导入gevent、time、requests
from gevent.queue import Queue
#从gevent库里导入queue模块

start = time.time()

url_list = ['https://www.baidu.com/',
'https://www.sina.com.cn/',
'http://www.sohu.com/',
'https://www.qq.com/',
'https://www.163.com/',
'http://www.iqiyi.com/',
'https://www.tmall.com/',
'http://www.ifeng.com/']

work = Queue()
#创建队列对象,并赋值给work。
for url in url_list:
#遍历url_list
    work.put_nowait(url)
    #用put_nowait()函数可以把网址都放进队列里。

def crawler():
    while not work.empty():
    #当队列不是空的时候,就执行下面的程序。
        url = work.get_nowait()
        #用get_nowait()函数可以把队列里的网址都取出。
        r = requests.get(url)
        #用requests.get()函数抓取网址。
        print(url,work.qsize(),r.status_code)
        #打印网址、队列长度、抓取请求的状态码。

tasks_list  = [ ]
#创建空的任务列表
for x in range(2):
#相当于创建了2个爬虫
    task = gevent.spawn(crawler)
    #用gevent.spawn()函数创建执行crawler()函数的任务。
    tasks_list.append(task)
    #往任务列表添加任务。
gevent.joinall(tasks_list)
#用gevent.joinall方法,执行任务列表里的所有任务,就是让爬虫开始爬取网站。
end = time.time()
print(end-start)

拓展知识:

我们的CPU已经有单核进化为了多核,每个核都能够独立运作。计算机开始能够真正意义上同时执行多个任务(术语叫并行执行),而不是在多个任务之间来回切换(术语叫并发执行)。

我们电脑一般都会是多核CPU。多协程,其实只占用了CPU的一个核运行,没有充分利用到其他核。利用CPU的多个核同时执行任务的技术,我们把它叫做“多进程”。

 

所以,真正大型的爬虫程序不会单单只靠多协程来提升爬取速度的。比如,百度搜索引擎,可以说是超大型的爬虫程序,它除了靠多协程,一定还会靠多进程,甚至是分布式爬虫。

  • 进程、线程、协程之间的关系:

协程在本质上只用到CPU的一个核。而多进程(multiprocessing库)爬虫允许你使用CPU的多个核。可以这么理解,一个核代表一个进程,而一个核即一个进程里面又有多个线程,同时,一个线程里面可以有多个协程。

线程是进程的子单位,都是实体;而协程从严格意义来讲,是组织好的代码流程, 协程需要线程来承载运行。多协程的爬取一般只用到一个进程的一个线程单位。

九、Scrapy框架

Scrapy是一套基于Twisted的异步处理框架,纯python实现的爬虫框架。在我们之前的爬虫基础中,我们要爬取需要的数据,需要按照爬虫操作流程导入和操作不同的模块。但Scrapy框架这些都自动实现了,我们可以根据需求定制开发几个模块,就能够轻松的实现一个爬虫。

1、Scrapy的结构

Scrapy的结构如下图所示:

在Scrapy框架中:

Scrapy Engine(引擎)是中心,其他组成部分由引擎调度。

Scheduler(调度器):主要负责处理引擎发送过来的requests对象(即网页请求的相关信息集合,包括params,data,cookies,request headers…等),会把请求的url以有序的方式排列成队,并等待引擎来提取(功能上类似于gevent库的queue模块)。

 

Downloader(下载器):是负责处理引擎发送过来的requests,进行网页爬取,并将返回的response(爬取到的内容)交给引擎。它对应的是爬虫流程【获取数据】这一步。

Spiders(爬虫):主要任务是创建requests对象和接受引擎发送过来的response(Downloader部门爬取到的内容),从中解析并提取出有用的数据。它对应的是爬虫流程【解析数据】和【提取数据】这两步。

 

Item Pipeline(数据管道):只负责存储和处理Spiders部门提取到的有用数据。这个对应的是爬虫流程【存储数据】这一步。

 

Downloader Middlewares(下载中间件):会提前对引擎发送的诸多requests做出处理。

 

Spider Middlewares(爬虫中间件):会提前接收并处理引擎发送来的response,过滤掉一些重复无用的东西。

Scrapy Engine负责数据和信号在不同模块之间的传递scrapy已实现
Scheduler一个存放引擎发过来的requests请求的队列scrapy已实现
Downloader下载引擎发送过来的请求,并返回给引擎scrapy已实现
Spider处理引擎发送过来的response,并返回给引擎需要自己实现
Item Pipeline处理引擎发送过来的数据,比如存储到excel表格需要自己实现
Downloader Middlewares可以自定义下载,设置代理,设置请求头等根据自己的需要实现
Spider MiddlewaresSpider可以自定义requests请求和response过滤根据自己的需要实现

在Scrapy里,整个爬虫程序的流程都不需要我们去操心,且Scrapy中的程序全部都是异步模式,所有的请求或返回的响应都由引擎自动分配去处理。

哪怕有某个请求出现异常,程序也会做异常处理,跳过报错的请求,继续往下运行程序。

详情见官方参考文档:https://doc.scrapy.org/en/latest/

 

 

2、Scrapy安装

Scrapy安装方法与python版本有关,如果python版本为python3.8以下版本,直接用常规pip安装方式:

windows: pip install scrapy

mac: pip3 install scrapy

如果python版本为python3.8则会报错,博客上也有详细Window10环境下的安装指南,详见下面链接:

//blog.csdn.net/Boy_Teacher/article/details/106537835

3、Scrapy使用流程

  1. 创建项目
  2. 定义数据
  3. 定义爬虫
  4. 修改设置
  5. 存储
  6. 运行

以爬取豆瓣Top250书籍信息(包含书名、出版信息、书籍评分)为例。https://book.douban.com/top250

1)创建项目

  • 要在本地电脑打开终端
  • cd xxx  进入要保存项目的目录下
  • 输入创建项目命令:python -m scrapy startproject douban(爬虫名)

即创建如下scrapy项目结构:

2)定义数据

定义数据是在items.py中,当我们每一次要记录数据的时候,都要记录“书名”,“出版信息”,“评分”。我们会实例化一个对象,利用这个对象来记录数据。每一次,当数据完成记录,它会离开spiders,来到Scrapy Engine(引擎),引擎将它送入Item Pipeline(数据管道)处理。

 

import scrapy
#导入scrapy
class DoubanItem(scrapy.Item):
#定义一个类DoubanItem,它继承自scrapy.Item
    title = scrapy.Field()
    #定义书名的数据属性
    publish = scrapy.Field()
    #定义出版信息的数据属性
    score = scrapy.Field()
    #定义评分的数据属性

第3行代码:我们定义了一个DoubanItem类。它继承自scrapy.Item类。

第5、7、9行代码:我们定义了书名、出版信息和评分三种数据。scrapy.Field()这行代码实现的是,让数据能以类似字典的形式记录。

 

 

3)定义爬虫

Scrapy中爬虫是在spiders目录下。我们可以在spiders这个文件夹里创建爬虫文件。我们新建一个爬虫文件,并把这个文件命名为top250.py。那么爬虫代码都需要在这个top250.py里编写。

top250.py中代码如下所示:

import scrapy
import bs4
from ..items import DoubanItem
# 需要引用DoubanItem,它在items里面。因为是items在top250.py的上一级目录,所以要用..items,这是一个固定用法。

class DoubanSpider(scrapy.Spider):
#定义一个爬虫类DoubanSpider。
    name = 'douban'
    #定义爬虫的名字为douban。
    allowed_domains = ['book.douban.com']
    #定义爬虫爬取网址的域名。
    start_urls = []
    #定义起始网址。
    for x in range(3):
        url = 'https://book.douban.com/top250?start=' + str(x * 25)
        start_urls.append(url)
        #把豆瓣Top250图书的前3页网址添加进start_urls。

    def parse(self, response):
    #parse是默认处理response的方法。
        bs = bs4.BeautifulSoup(response.text,'html.parser')
        #用BeautifulSoup解析response。
        datas = bs.find_all('tr',class_="item")
        #用find_all提取<tr class="item">元素,这个元素里含有书籍信息。
        for data in  datas:
        #遍历data。
            item = DoubanItem()
            #实例化DoubanItem这个类。
            item['title'] = data.find_all('a')[1]['title']
            #提取出书名,并把这个数据放回DoubanItem类的title属性里。
            item['publish'] = data.find('p',class_='pl').text
            #提取出出版信息,并把这个数据放回DoubanItem类的publish里。
            item['score'] = data.find('span',class_='rating_nums').text
            #提取出评分,并把这个数据放回DoubanItem类的score属性里。
            print(item['title'])
            #打印书名。
            yield item
            #yield item是把获得的item传递给引擎。

代码中定义了一个爬虫类DoubanSpider(Douban是项目名)。DoubanSpider类继承自scrapy.Spider类。

 

name是定义爬虫的名字,这个名字是爬虫的唯一标识。name = 'douban'意思是定义爬虫的名字为douban。等会我们启动爬虫的时候,要用到这个名字。

 

allowed_domains是定义允许爬虫爬取的网址域名(不需要加https://)。如果网址的域名不在这个列表里,就会被过滤掉。allowed_domains就限制了,我们这种关联爬取的URL,一定在book.douban.com这个域名之下,不会跳转到某个奇怪的广告页面。

 

start_urls是定义起始网址,就是爬虫从哪个网址开始抓取。在此,allowed_domains的设定对start_urls里的网址不会有影响。后续关联网址如果不在allowed_domains里,则不会被爬取。

 

parse是Scrapy里默认处理response的一个方法,爬取解析网页数据。网页请求requests.get()这一步已经由Scrapy框架自动完成。

 

4)设置

我们用代码编写好了一个爬虫。不过,实际运行的话,可能还是会报错。原因在于Scrapy里的默认设置没被修改。比如我们需要修改请求头。点击settings.py文件,你能在里面找到如下的默认设置代码:

# Crawl responsibly by identifying yourself (and your website) on the user-agent
#USER_AGENT = 'douban (+http://www.yourdomain.com)'

# Obey robots.txt rules
ROBOTSTXT_OBEY = True

把USER _AGENT的注释取消(删除#),然后替换掉user-agent的内容,就是修改了请求头。

又因为Scrapy是遵守robots协议的,如果是robots协议禁止爬取的内容,Scrapy也会默认不去爬取,所以我们还得修改Scrapy中的默认设置。把ROBOTSTXT_OBEY=True改成ROBOTSTXT_OBEY=False,就是把遵守robots协议换成无需遵从robots协议,这样Scrapy就能不受限制地运行。

关于设置的详细说明请见如下文档:

https://blog.csdn.net/u011781521/article/details/70188171

5)存储

一、存储为CSV格式

只需在settings.py文件里,添加如下的代码即可。

FEED_URI='./storage/data/%(name)s.csv'
FEED_FORMAT='CSV'
FEED_EXPORT_ENCODING='ansi'

FEED_URI是导出文件的路径。'./storage/data/%(name)s.csv',就是把存储的文件放到与main.py文件同级的storage文件夹的data子文件夹里。

FEED_FORMAT 是导出数据格式,写CSV就能得到CSV格式。

FEED_EXPORT_ENCODING 是导出文件编码,ansi是一种在windows上的编码格式,你也可以把它变成utf-8用在mac电脑上。

二、存储为excel表格

我们使用openpyxl模块。

我们需要先在settings.py里设置启用ITEM_PIPELINES,设置方法如下:只要取消ITEM_PIPELINES的注释(删掉#)即可。

#需要修改`ITEM_PIPELINES`的设置代码:

# Configure item pipelines
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
#ITEM_PIPELINES = {
#     'jobui.pipelines.jobuiPipeline': 300,
# }

 

 

接着,我们就可以去编辑pipelines.py文件。

 

import openpyxl

class DoubanPipeline(object):
#定义一个DoubanPipeline类,负责处理item
    def __init__(self):
    #初始化函数 当类实例化时这个方法会自启动
        self.wb =openpyxl.Workbook()
        #创建工作薄
        self.ws = self.wb.active
        #定位活动表
        self.ws.append(['书名', '出版信息', '评分'])
        #用append函数往表格添加表头
        
    def process_item(self, item, spider):
    #process_item是默认的处理item的方法,就像parse是默认处理response的方法
        line = [item['title'], item['publish'], item['score']]
        #把书名、出版信息和评分都写成列表的形式,赋值给line
        self.ws.append(line)
        #用append函数把书名、出版信息和评分的数据都添加进表格
        return item
        #将item丢回给引擎,如果后面还有这个item需要经过的itempipeline,引擎会自己调度

    def close_spider(self, spider):
    #close_spider是当爬虫结束运行时,这个方法就会执行
        self.wb.save('./DoubanTop250.xlsx')
        #保存文件
        self.wb.close()
        #关闭文件

 

 

6)运行

方法一:

在本地电脑的终端跳转到scrapy项目的文件夹(跳转方法:cd+文件夹的路径名),

然后输入命令行:python -m scrapy crawl douban(douban 就是我们爬虫的名字)。

方法二:

在最外层的大文件夹里新建一个main.py文件(与scrapy.cfg同级)。在main.py文件里输入以下代码:

from scrapy import cmdline
import sys,os
dirpath=os.path.dirname(os.path.abspath(__file__))
print(dirpath)
sys.path.append(dirpath)
os.chdir(dirpath)
cmdline.execute(['scrapy','crawl','jobui'])

在cmdline模块中,有一个execute方法能执行终端的命令行,不过这个方法需要传入列表的参数。我们想输入运行Scrapy的代码scrapy crawl douban,就需要写成['scrapy','crawl','douban']这样。

 

十、爬虫进阶方向与反爬虫

1、爬虫进阶方向

现在学会了爬虫的基础操作,爬虫学习进阶可以考虑从下面几个方面做提升:

1)解析与提取

爬虫解析模块除了基础操作中用到的BeautifulSoup解析、Selenium自带解析库,此外还会有xpath、lxml等。

数据的解析提取中有个很强大的工具正则表达式(re模块),它可以让我们自己设定一套复杂的规则,然后把目标文本里符合条件的相关内容给找出来。

2)存储

我们目前已经使用过的是csv和excel。它们并不是非常高难度的模块,可以去翻阅它们的官方文档,了解更多的用法。

当数据量变得十分巨大,同时数据与数据之间的关系,应难以用一张简单的二维平面表格来承载。那么,我们需要数据库的帮助。学习数据库,需要去接触另一种语言:SQL。

3)数据分析与可视化

可以学习了解模块与库:Pandas/Matplotlib/Numpy/Scikit-Learn/Scipy。进行数据分析与可视化相关的操作。

4)更多的爬虫

当有太多的数据要爬取,就要开始关心爬虫的速度。我们现在只涉及到了协程。协程在本质上只用到CPU的一个核。而多进程(multiprocessing库)爬虫允许我们使用CPU的多个核,所以我们可以使用多进程,或者是多进程与多协程结合的方式进一步优化爬虫。

可以去了解分布式爬虫相关的知识,我们去创建一个共享的队列,队列里塞满了待执行的爬虫任务。让多个设备从这个共享队列当中,去获取任务,并完成执行。

我们已经简单地学过Scrapy框架的基本原理和用法。而一些更深入的用法:使用Scrapy模拟登录、存储数据库、使用HTTP代理、分布式爬虫……

先去更深入地学习、了解Scrapy框架。然后再了解一些其他的优秀框架,如:PySpider。

5)更多的项目实践

 

 

 

 

 

 

 

 

2、反爬虫

1)请求头

2)限制登录

3)复杂的交互,如验证码

4)IP限制

......

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值