Scrapy爬虫笔记

Scrapy框架原理

scrapy爬虫执行示意图:
image
Scrapy主要包括了以下组件:

  • 引擎(Scrapy)
    用来处理整个系统的数据流处理, 触发事务(框架核心)。
  • 调度器(Scheduler)
    用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址。
  • 下载器(Downloader)
    用于下载网页内容, 并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的)。
  • 爬虫(Spiders)
    爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面。
  • 项目管道(Pipeline)
    负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。
  • 下载器中间件(Downloader Middlewares)
    位于Scrapy引擎和下载器之间的框架,主要是处理Scrapy引擎与下载器之间的请求及响应。
  • 爬虫中间件(Spider Middlewares)
    介于Scrapy引擎和爬虫之间的框架,主要工作是处理蜘蛛的响应输入和请求输出。
  • 调度中间件(Scheduler Middewares)
    介于Scrapy引擎和调度之间的中间件,从Scrapy引擎发送到调度的请求和响应。

Scrapy运行流程大概如下:

  1. 引擎从调度器中取出一个链接(URL)用于接下来的抓取
  2. 引擎把URL封装成一个请求(Request)传给下载器
  3. 下载器把资源下载下来,并封装成应答包(Response)
  4. 爬虫解析Response
  5. 解析出实体(Item),则交给实体管道进行进一步的处理
  6. 解析出的是链接(URL),则把URL交给调度器等待抓取

个人认为,爬虫的流程就是,当开始执行爬虫时,引擎从被执行的爬虫中获取第一个页面,交给调度器,调度器将网址入栈,同时出栈一个网址(其实在最开始,就是出栈刚刚放进去的网址)交给下载器去下载,下载完成后,交给爬虫进行清洗,最后将需要爬取的数据封装在item中,交给管道进行持久化。
所以爬虫(特指爬虫模块)的任务就是清晰数据,将数据封装进item中,同时实现翻页等功能;下载器是下载调度器给定的网址,作为response传给爬虫;管道的任务是将数据持久化;item是被爬取数据的实体。当然实际上在爬虫和管道中,可以干任何你相干的事,比如在爬虫中将数据持久化为一个txt文本,但这会影响爬虫的效率。
通过这些分析可知,在编写一个简单的爬虫时,只需要编写item,spiders,pip这三个文件。

Scrapy框架使用步骤

  1. 安装scrapy框架
    使用cmd安装,命令如下:
pip install scrapy
  1. 创建爬虫项目
    同样使用cmd进行创建,在想要防止爬虫的文件夹打开爬虫,执行下面的命令:
scrapy startproject HuyaSpider  # 创建一个爬虫项目,HuyaSpider是爬虫项目的名字。

cd HuyaSpider  # 切换到刚创建的爬虫项目下。

scrapy genspider huya "huya.com"  # 创建一个爬虫,
# 第一参数是爬虫的名字,自己随便定义,注意不要与爬虫项目相同,
# 第二个参数是爬取的域名,超过这个域名的链接不会被下载器下载
# 一个爬虫项目可以有多个爬虫。
  1. 项目结构
    创建后的项目就是一个文件夹,结构如下:
HuyaSpider/
   scrapy.cfg

   HuyaSpider/
       __init__.py
       items.py
       middlewares.py
       pipelines.py
       settings.py

       spiders/
           __init__.py
           huya.py
           *其他爬虫.py

在settings.py中进行爬虫的设置,如headsers配置,管道的开启和关闭;在item.py中抽象要爬取的内容;在管道中进行数据持久化。

示例

settings.py:

# 设置默认的headers
DEFAULT_REQUEST_HEADERS = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36'
}


# 关闭或开启管道,注释状态下就是关闭。
# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
#ITEM_PIPELINES = {
#    'HuyaSpider.pipelines.HuyaspiderPipeline': 300,
#}

item.py:

# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html

import scrapy


class HuyaspiderItem(scrapy.Item):
    live_name = scrapy.Field()  # 虎牙直播间名称
    live_url = scrapy.Field()  # 直播间网址
    live_img_url = scrapy.Field()  # 直播间封面网址
    live_appearance_level = scrapy.Field()  # 颜值打分

huya.py:

# encoding:UTF-8 #
import scrapy
from HuyaSpider.items import HuyaspiderItem
from HuyaSpider.AppearanceLevel import BaiduAI

# 创建自scrapy.Spider 最基础类
class HuyaSpider(scrapy.Spider):
    name = 'huya'  # 爬虫名字,必须唯一
    allowed_domains = ['huya.com']  # 允许采集的域名
    start_urls = ['https://www.huya.com/g/2168']  # 开始采集的网站
    count = 0  # 计数器,记录爬取了几个直播间

    # 解析响应数据,提取网址或数据,response就是响应数据
    def parse(self, response):  # 提取数据 selector选择器 正则、BeautyfulSoup、xpath、css
        live_rooms = response.xpath('//div[@class="box-bd box-live-card-list"]/ul/li')  # 注意没有.extract()方法,这里提取了的话,下面的for循环中就无法使用xpath语法提取了。

        for liveRoom in live_rooms:
            huyaspiderItem = HuyaspiderItem()  # 创建一个要爬取的item的对象

            huyaspiderItem["live_name"] = liveRoom.xpath("./a/img[@class='pic']/@title").extract()[0]  # 直播间名称
            huyaspiderItem["live_url"] = liveRoom.xpath("./a/@href").extract()[0]  # 直播间地址
            huyaspiderItem["live_img_url"] = liveRoom.xpath("./a/img[@class='pic']/@data-original").extract()[0]  # 直播间封面网址

            # 实例化颜值测试类
            live_appearance_level = BaiduAI(huyaspiderItem["live_img_url"])

            # 测试颜值
            huyaspiderItem["live_appearance_level"] = live_appearance_level.face_identification()  # 封面颜值

            HuyaSpider.count += 1  # 打印测试数量
            print(HuyaSpider.count)
            yield huyaspiderItem  # 将item交给pip持久化

piplines.py:
这个项目最后没有选择用管道持久化数据,因为目标是将采集到的数据保存为一个.csv文件,而scrapy框架提供的简便的命令实现。

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html


# useful for handling different item types with a single interface
from itemadapter import ItemAdapter
from HuyaSpider.AppearanceLevel import BaiduAI


class HuyaspiderPipeline:
    def process_item(self, item, spider):

        print('直播间名称: ' + item["live_name"])
        print('直播间网址: ' + item["live_url"])
        print('直播间封面照网址: ' + item["live_img_url"])
        print('颜值: ' + item["live_appearance_level"])

        return item  # 管道是有优先级的,可以在settings中设置,如果不return,下面的管道就接收不到item。

颜值测试类AppearanceLevel.py:
这个类和爬虫没关系,只是为了实现功能,放在了项目中,可以是为一个库。

# encoding:utf-8
import base64
import json
import requests

'''
通过百度人脸识别api测试颜值
'''
class BaiduAI:
    def __init__(self, img):
        self.AK = "v16G2mjnKu3ypaKcAShKkLhK"  # 应用API Key
        self.SK = "1clCBt8Mo08aUxIRs8z0rRi1VPz6uWxb"  # 应用Secret Key
        self.img_src = img
        self.headers = {
            "Content-Type": "application/json; charset=UTF-8"
        }

    def get_AccessToken(self):
        # 获取Access Token
        host = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=' + self.AK + '&client_secret=' + self.SK
        response = requests.get(host, headers=self.headers)
        json_result = json.loads(response.text)
        if response:
            return json_result['access_token']
        else:
            print(json_result)
            return 0

    def face_identification(self):
        # 人脸检测与属性分析
        # img = self.img_to_base64(self.img_src)
        request_url = "https://aip.baidubce.com/rest/2.0/face/v3/detect"
        post_data = {
            "image": self.img_src,
            "image_type": "URL",
            "face_field": "gender,age,beauty,gender,race,emotion,face_shape,landmark",# 包括age,beauty,expression,face_shape,gender,glasses,landmark,emotion,face_type,mask,spoofing信息
            "face_type": "LIVE"# 人脸的类型。LIVE表示生活照,IDCARD表示身份证芯片照,WATERMARK表示带水印证件照,CERT表示证件照片,默认LIVE。
        }

        access_token = self.get_AccessToken()
        request_url = request_url + "?access_token=" + access_token
        response = requests.post(url=request_url, data=post_data, headers=self.headers)
        json_result = json.loads(response.text)
        # print(json_result)
        if json_result['error_code'] == 0:
            # print("人脸表情:", json_result['result']['face_list'][0]['emotion']['type'])
            # print("人物年龄:", json_result['result']['face_list'][0]['age'])
            return json_result['result']['face_list'][0]['beauty']
            # print("人物性别:", json_result['result']['face_list'][0]['gender']['type'])
            # print("人物种族:", json_result['result']['face_list'][0]['race']['type'])
            # print("人物特征点位置:", json_result['result']['face_list'][0]['landmark72'])
        else:
            print(json_result['error_code'])
            print(json_result['error_msg'])

爬虫的流程是,引擎首先将huya爬虫中的类变量https://www.huya.com/g/2168作为开始地址放入调度器;调度器再将这个网址交给下载器;下载器下载网页源码后,作为response参数交给huya爬虫,随后爬虫使用xpath清晰数据,获取这个页面的全部直播间封面网址、直播间名称、直播间网址。随后进入列表遍历,在yield执行时,huyaSpiderItem被保存,并作为item参数交给管道;管道中的代码执行完后,huya爬虫返回现场,继续执行for循环,将列表中的下一项装入item中,直到列表最后一项被装入。

项目使用方法

在爬虫项目的文件夹下打开cmd,执行以下代码:

scrapy crawl huya -o huya.csv
# 自动将爬取的结果保存为名为huya.csv的csv文件,位置在爬虫项目文件夹下。
# huya是爬虫的名字,不是整个项目的,一个项目可以有多个爬虫。
# huya.csv的名字可以随便定义。

注意事项

yield

yield的作用和return很像,使用了yield的函数称为generator(生成器)。但与return不同的是,yield的在返回变量后,会保存现场,下次执行函数时,函数中的局部变量的值不会归零。示例代码如下:

def test():
    a = 0
    yield a = a + 1

执行:
test()
test()

结果如下:
1
2
导入类

在可以看到,在爬虫文件huya.py中引入了item类,但是在书写 from 文件夹.文件名 import 类名 代码时,却提示无法找到模块。解决办法是右键选择items所在的文件夹,在Mark Diretory as菜单项中选则将项目设置为Source Root。如果还不行,就将再上一级的文件夹设为Source Root。
如果依旧无法解决,依次打开File->Settings->Build, Execution, Deployment->Console->Python Console->Configure Interpreters勾选Add source roots to PYTHONPATH。

xpath语法

表达式描述
nodename选取此节点的所有子节点。
/从根节点选取。
//从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。
.选取当前节点。
..选取当前节点的父节点。
@选取属性。

html示例:

<?xml version="1.0" encoding="ISO-8859-1"?>

<bookstore>

<book id = "1">
  <title lang="eng">Harry Potter</title>
  <price>29.99</price>
</book>

<book class = "2">
  <title lang="eng">Learning XML</title>
  <price>39.95</price>
</book>

</bookstore>

xpath示例:

路径表达式结果
bookstore选取 bookstore 元素的所有子节点。
/bookstore选取根元素 注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径!
bookstore/book选取属于 bookstore 的子元素的所有 book 元素。
//book选取所有名为book的子元素,而不管它们在文档中的位置。
bookstore//book选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于bookstore之下的什么位置。
//@lang选取名为 lang 的所有属性。
//book[@id=“1”]选取所有名为book,id为1的元素
//book[@class=“1”]选取所有名为book,class为1的元素
//book[@id=“1”]/price/@text选取所有名为book,id为1的元素下的price元素中的文本。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值