爬虫scrapy

在这里插入图片描述

零, scrapy框架必读

学习scrapy前, 我们需要看懂下图
scrapy框架流程图
请务必仔细阅读并理解该流程

0- spider优先提供一个起始url(start_url)和想要爬取的域名, 发送给引擎。
1- scrapy框架核心为引擎(ScrapyEngine)。启动scrapy时。引擎会读取spiders中由start_url构建出的request请求。
2- Engine 将封装好的request推送到 Scheduler调度器中 。调度器使用过滤器读取setting中的配置构成请求队列返回给引擎。
3- 引擎按照 Scheduler中的安排下发任务给 Downloader下载器 下载器请求Internet去获取相应的响应
4- 其中Requests和Response都会经过DownloadMiddlerwares,一些特殊请求或响应可以在中间件中构建或处理完成 。
5- 引擎获取到Downloader中返回的Response后,将response传递给spider。 spider通过parse方法对response进行解析
6- 获取到解析数据后,spider将数据返回给引擎。 如果还有回调函数。spider将url重新封装成Request再次返回给引擎
7- 引擎将所有的Request调用完毕, 结束流程

总结

scrapy是一个自动化的框架, 它给我们提供了 Spiders, Engine, Scheduler, Downloader,
Pipelines 以及 Middleware功能。-其中我们只用编写或修改部分功能。 如
1- Spiders功能中, 我们需要编写 【提取和构建 requests & Response 数据处理 】
2- setting中配置Scheduler需要的参数
3- DownloaderMiddleware中构建请求的特殊参数。 如(UA, IP池等)
4- Pipline中做结果处理操作。如(数据入库)

note: 我们所有的步骤全部又Engine调度

一, 环境准备

1- python 解释器
2- pycharm 编辑工具
3- scarpy 安装scrapy包

note: 这里有个坑。 我们正常安装的的scrapy版本与scrappy-redis可能会有冲突。所以我们通常会固定scrapy版本。如果以后要使用scrapy-redis的话, 建议按照如下操作更新scrapy。
在这里插入图片描述

执行一下步骤:
pip uninstall scrapy 输入:y确认卸载
pip install scrapy2.5.1 安装指定的scrapy版本
scrapy version --verbose 查看scrapy中各个工具的版本。
pip uninstall cryptography 如果cryptography版本不在 1.. 则卸载 cryptography
pip install cryptography
36.0.2 固定版本36.0.2
至此。 scrapy版本即固定成功

一切准备就绪,我们开始创建自己的第一个scrapy爬虫

二, 创建一个最简单的爬虫项目

1- 选择要存放scrapy的目录, 进入cmd目录地址, 使用该命令创建scrapy项目
	scrapy startproject ant_spider

在这里插入图片描述

2- cd进入scrapy项目中, 并指定我们要爬取的网站。
cd ant_spider
scrapy genspider yanzhaopro https://yz.chsi.com.cn
此时我们指定了要爬取 https://yz.chsi.com.cn 下的内容
![在这里插入图片描述](https://img-blog.csdnimg.cn/6baa8ba8d916448681633a1c31fb51e0.png)

此时我们的scrapy项目创建成功

3- scrapy配置

1, scrapy是一个爬虫框架, 即我们只需要做出相应的配置, 它就能按照指定的步骤去执行爬虫步骤。

首先我们看下setting中的配置:

1-  我们当然不应该遵守 Robots 协议
	ROBOTSTXT_OBEY = False
	note: 如果不清楚robots协议还是要去百度一下, 简单来说就是互联网中的君子协议。 如果遵守就没必要学习爬虫了。
	
2- 请求并发量
	CONCURRENT_REQUESTS = 32
	note: 如果目标网站数据不敏感当然可以高一点(可以达到200。其实可以更高, 鉴于数据不敏感的网站服务器一般不会太牛, 200差不多了, 再高可能会影响爬虫的成功率)。 但是目标网站过于敏感还是建议使用低并发处理(50以下)
	
3- 下载延时
	DOWNLOAD_DELAY = 3
	note: 这个参数可以高一点。 我们请求成功就行, 如果因为下载太多频繁导致成功率太低得不偿失(3-10之间都可以)
	
4- 域名和ip的并发量
	CONCURRENT_REQUESTS_PER_DOMAIN = 16
	CONCURRENT_REQUESTS_PER_IP = 16
	note: 这两个是配套使用, 具体用法可以百度。 参数调节一般二者选一个就行。值尽量在20-50之间
	
5- cookie的使用
	COOKIES_ENABLED = False
	note: 这里其实也是大有学问, 本质就是是否使用scrapy配置的cookie, 或者手动添加cookie。 
	值为False: 不会自动添加cookie, 如果需要用cookie,则手动添加
	值不为False(为True或者注释掉): 自动添加cookie
	
6- 开启控制台调试打印
	TELNETCONSOLE_ENABLED = True
	
7- 给定默认请求头
	DEFAULT_REQUEST_HEADERS = {
	  'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
	  'Accept-Language': 'en',
	}
	
8- 开启middleware
	SPIDER_MIDDLEWARES = {
	   'ant_spiderpro.middlewares.AntSpiderproSpiderMiddleware': 543,
	}
	DOWNLOADER_MIDDLEWARES = {
	   'ant_spiderpro.middlewares.AntSpiderproDownloaderMiddleware': 543,
	}
	
9- item操作
	ITEM_PIPELINES = {
	   'ant_spiderpro.pipelines.AntSpiderproPipeline': 300,
	}
10- 设置日志等级
LOG_LEVEL = "WARNING"
4- 正式编写一个scrapy项目

到这一步我们就可以正式编写一个scrapy了
还记得第一步吗, spider中构建一个request请求。
找到spider
在这里插入图片描述
编写spider代码如下

import scrapy
from scrapy.http import Request


class YanzhaoproSpider(scrapy.Spider):
    name = 'yanzhaopro'

    def __init__(self):
        # 需要爬取的页面, 这里可以添加url池
        self.start_urls = ['http://yz.chsi.com.cn/']

    def start_requests(self):
        if not hasattr(self, 'start_urls'):
            self.start_urls = []

        for url in self.start_urls:
            print("我们开始构建请求")
            yield Request(url, dont_filter=True)

    def parse(self, response):
        print("我们对response进行处理")
        print(response.text)
5- 启动爬虫

此时有几种方式启动spider, 我这里只说一种。
在项目中创建一个 start_spider 的py文件

from scrapy import cmdline

cmdline.execute(['scrapy', 'crawl', 'yanzhaopro'])

执行既能启动
在这里插入图片描述

三,案例一 ==> 单页面爬取

1- setting中的初级配置

BOT_NAME = 'firstcrawl'

SPIDER_MODULES = ['firstcrawl.spiders']
NEWSPIDER_MODULE = 'firstcrawl.spiders'

# 设置日志等级
LOG_LEVEL = "WARNING"

# Crawl responsibly by identifying yourself (and your website) on the user-agent
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36'

# Obey robots.txt rules
ROBOTSTXT_OBEY = False

# Configure maximum concurrent requests performed by Scrapy (default: 16)
# scrapy默认是协程任务
CONCURRENT_REQUESTS = 32

# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
    # 管道: 优先级
   'firstcrawl.pipelines.FirstcrawlPipeline': 300,
}

2- spider中的代码

import scrapy


class XiaoyouxiSpider(scrapy.Spider):
    name = 'xiaoyouxi'                              # 爬虫的名字
    allowed_domains = ['4399.com']                  # 爬虫允许访问的域名是什么
    start_urls = ['https://www.4399.com/flash/']          # 起始 url

    def parse(self, response):
        print(response.text)
        print("================================================================")

        # scrapy 中封装了lxml, 可以直接使用 .xpath 方法
        li_list = response.xpath("//ul[@class='n-game cf']/li")
        for li in li_list:
            # scrapy中的xpath不是调用的 lxml 包中的xpath。
            # 框架中的xpath提取的是一个选择器(Selector)。 从选择器中提取数据需要使用: extract()  /  extract_first()
            # extract() 为提取全部
            # extract_first() 为提取第一个
            game_name = li.xpath("./a/b/text()").extract_first()
            game_type = li.xpath("./em/a/text()").extract_first()
            game_img = "https:" + li.xpath("./a/img/@lz_src").extract_first()
            game_time = li.xpath("./em[last()]/text()").extract_first()

            # 返回数据而不打断程序的执行
            # ERROR: Spider must return request, item, or None
            yield {
                "game_name": game_name,
                "game_type": game_type,
                "game_img": game_img,
                "game_time": game_time,
            }

note:其中特别说明的是 scrapy 中使用xpath不是基于 lxml 库。 所以用法上有点区别
为了解决xpath提取文本时以索引获取数据可能报错的问题
如果使用[0]索引取值。 页面中没有该值则会报错【li.xpath(“./a/b/text()”)[0]】
scrapy将xpath提取数据封装成了selector选择器, 当xpath中的对象不存在时。 返回为None, 避免流程无故中断
而从选择器中取值提供了两个方法: 【extract_first()】取第一个值。 【extract()】 提取全部值

3- pipline中的代码

# 管道使用。必须在settings中开启
class FirstcrawlPipeline:
    # item 数据
    # process 处理

    def process_item(self, item, spider):
        print("这里是管道: ", item)

        with open("data.csv", mode="a", encoding="utf-8") as f:
            f.write(f"{item['game_name']}, {item['game_type']}, {item['game_img']}, {item['game_time']}\n")

        return item

返回结果如下
在这里插入图片描述

四, 案例二 ==> 多次请求和下载图片

以该url: 【https://desk.zol.com.cn/dongman/】 为例。 我们需要获取页面上的img详情链接, 请求详情链接中的图片进行下载
此时步骤为:
…1- 第一次请求获取页面上的img_url的详情地址。
…2- 第二次请求从详情页面中获取多个图片地址。
…3-最后针对每个url进行下载

1- setting.py中的代码

BOT_NAME = 'tupian'

SPIDER_MODULES = ['tupian.spiders']
NEWSPIDER_MODULE = 'tupian.spiders'


USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36'

ROBOTSTXT_OBEY = False
LOG_LEVEL = "WARNING"


ITEM_PIPELINES = {
   'tupian.pipelines.DetailTupianPipeline': 301,
}

IMAGES_STORE = "./jia"
MEDIA_ALLOW_REDIRECTS = True

2- spider中的代码

import scrapy
from scrapy.http import Request


class JiaSpider(scrapy.Spider):
    name = 'jia'
    allowed_domains = ['zol.com.cn']
    start_urls = ['https://desk.zol.com.cn/dongman/']

    def parse(self, response, **kwargs):
        print(response.url)
        print("====================================================")
        hrefs = response.xpath("//ul[@class='pic-list2  clearfix']/li/a/@href").extract()
        for href in hrefs:
            # 屏蔽无用链接
            if href.endswith(".exe"):
                continue

            # 拼接出完整的路径
            """
            response路径:
                https://desk.zol.com.cn/dongman/
            
            如果抓取路径以“/”开头, 则需要与域名拼接成完整路径:
                https://desk.zol.com.cn/dongman/
                /bizhi/9109_111583_2.html
                ===》 https://desk.zol.com.cn/bizhi/9109_111583_2.html
                
            如果抓取路径不是以“/”开头, 则需要与response中抓取的同级目录进行拼接:
                https://desk.zol.com.cn/dongman/zhoujielun
                bizhi/9109_111583_2.html
                ===》 https://desk.zol.com.cn/dongman/bizhi/9109_111583_2.html
            """
            download_url = response.urljoin(href)
            print(download_url)

            yield Request(download_url, callback=self.detail_parse)

    def detail_parse(self, detail_response, **kwargs):

        img_url = detail_response.xpath("//img[@id='bigImg']/@src").extract_first()
        print(img_url)
        yield {
            "img_url": img_url
        }

3- pipline中的代码

from itemadapter import ItemAdapter
from scrapy.pipelines.images import ImagesPipeline
from scrapy.pipelines.files import FilesPipeline

from scrapy import Request


class TupianPipeline:
    def process_item(self, item, spider):
        return item


class DetailTupianPipeline(ImagesPipeline):
    # 发送下载请求
    def get_media_requests(self, item, info):
        img_url = item['img_url']
        print("来", img_url)
        # 请求对象船只的最佳方案: meta
        yield Request(img_url, meta={"img_path": img_url})

    def file_path(self, request, response=None, info=None, *, item=None):
        # 需要返回文件路径
        img_path = request.meta["img_path"]
        img_name = img_path.split("/")[-1]
        return f"save_img/{img_name}"

    def item_completed(self, results, item, info):
        # 对下载结果(results)进行判断, 选择操作
        return item

结果展示
在这里插入图片描述

步骤说明:
1- spider构建出Request后, 获取response响应。
2- spider中的parse方法解析response后提取出img_url详情页面。 构建出二次请求的Request并回调到 detail_parse 进行解析
【yield Request(download_url, callback=self.detail_parse)】
3- detail_parse 解析出详情页中的img图片, 在管道中进行下载
4- 管道的图片下载有三个函数, 分别是【构建下载请求】 【下载路径处理】 【下载结果逻辑】
图片下载完成

五, 案例三 ==> 请求翻页

1- 只有spider中的代码需要改动

import scrapy
from scrapy import Request


class FenleiSpider(scrapy.Spider):
    name = 'fenlei'
    allowed_domains = ['17k.com']
    start_urls = ['https://www.17k.com/all/book/2_0_0_0_0_0_0_0_1.html']

    def parse(self, response, **kwargs):
        book_list = response.xpath("//table/tbody/tr[contains(@class, 'bg')]")
        for book in book_list:
            book_type = book.xpath("./td[@class='td2']/a/text()").extract_first()
            book_name = book.xpath("./td[@class='td3']//a/text()").extract_first()
            book_chapters = book.xpath("./td[@class='td4']/a/text()").extract_first()
            book_author = book.xpath("./td[@class='td6']/a/text()").extract_first()
            yield {
                "book_type": book_type,
                "book_name": book_name,
                "book_chapters": book_chapters,
                "book_author": book_author
            }

        hrefs = response.xpath("//div[@class='page']/a/@href").extract()
        for href in hrefs:
            if href.startswith('javascript'):
                continue
            next_url = response.urljoin(href)
            yield Request(next_url, callback=self.parse)

六, 案例四 ==> 常规登录案例

1- setting中代码

BOT_NAME = 'loginpro'

SPIDER_MODULES = ['loginpro.spiders']
NEWSPIDER_MODULE = 'loginpro.spiders'

USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36'

ROBOTSTXT_OBEY = False
LOG_LEVEL = "WARNING"

COOKIES_ENABLED = False

DEFAULT_REQUEST_HEADERS = {
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Language': 'en',
    # 这里的cookie不可以直接使用, 因为scrapy中有个cookie的中间件会刷新这里的cookie
    "Cookie": 'GUID=c2a0dd44-3d59-4b1c-a17f-5f4f1c0e33bf; c_channel=0; c_csc=web; accessToken=avatarUrl%3Dhttps%253A%252F%252Fcdn.static.17k.com%252Fuser%252Favatar%252F09%252F49%252F49%252F99704949.jpg-88x88%253Fv%253D1670250949000%26id%3D99704949%26nickname%3DMrxiong1223%26e%3D1685804209%26s%3D1ed4b1d810f2a640; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%2299704949%22%2C%22%24device_id%22%3A%22184e2b4d45529f-056b1f117d50f2-58412b14-2073600-184e2b4d456d6a%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E7%9B%B4%E6%8E%A5%E6%B5%81%E9%87%8F%22%2C%22%24latest_referrer%22%3A%22%22%2C%22%24latest_referrer_host%22%3A%22%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%7D%2C%22first_id%22%3A%22c2a0dd44-3d59-4b1c-a17f-5f4f1c0e33bf%22%7D; Hm_lvt_9793f42b498361373512340937deb2a0=1670250813,1670720818; __bid_n=184feb8ad3b63e15874207; FEID=v10-7dfe2cb3a52c946f7bbaf83b0d75a37cdf23252d; __xaf_fpstarttimer__=1670720826804; __xaf_ths__={"data":{"0":1,"1":86400,"2":60},"id":"2601b593-4337-4646-99ed-4cf75e8c080c"}; __xaf_thstime__=1670720826807; FPTOKEN=hhpJOar3Pm4F2QF3tBjIjCg9lkLA2F9Mm71+WEXfUwZ1N+pR9tHaRmQnQVmQ2NgQa0bZb2Ntpxuhtqg4Mg0IBjfB07vqq4z/gzR+++P0jmy/02QTrdlsdljF+AU5emWnGGTdobRj0/9YagyuEdK1sY42ZQiSZYCzOjiYxeoCMEaalSB6k2eyTmgIYlGE4IOeq5DjWK4PYfHProC3piqDWRtApvDQzUmJBySmkFyFQgibQ6YgVht01ZpX+1mGa5o2E1I9uASyQZOeUNMHBiRg097fF05z+chXLoTyaC3rxUmCW55cLXooXV1o1rlzDGDw9Br+6eW+Ha3pMBmOS+Xq3aSNqmnldT1z3pQv9YCl0l5dnhqJeeSceqsYElJG9ACppLEYwiQ2EWxmFBI3wo3ZPQ==|UJQeL0Hjl5mZHzA4CKG3sKH7UoEiBkRXqz3XxPbtPcw=|10|195946289e4b100f86abdbadf9399cd6; __xaf_fptokentimer__=1670720826905; Hm_lpvt_9793f42b498361373512340937deb2a0=1670730045'
}

或者也可以使用下面的方式将cookie传入request

import scrapy
from scrapy import Request


class MyloginSpider(scrapy.Spider):
    name = 'mylogin'
    allowed_domains = ['17k.com']
    start_urls = ['https://user.17k.com/www/bookshelf/']

    def start_requests(self):
        
        cookie = 'GUID=c2a0dd44-3d59-4b1c-a17f-5f4f1c0e33bf; c_channel=0; c_csc=web; accessToken=avatarUrl%3Dhttps%253A%252F%252Fcdn.static.17k.com%252Fuser%252Favatar%252F09%252F49%252F49%252F99704949.jpg-88x88%253Fv%253D1670250949000%26id%3D99704949%26nickname%3DMrxiong1223%26e%3D1685804209%26s%3D1ed4b1d810f2a640; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%2299704949%22%2C%22%24device_id%22%3A%22184e2b4d45529f-056b1f117d50f2-58412b14-2073600-184e2b4d456d6a%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E7%9B%B4%E6%8E%A5%E6%B5%81%E9%87%8F%22%2C%22%24latest_referrer%22%3A%22%22%2C%22%24latest_referrer_host%22%3A%22%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%7D%2C%22first_id%22%3A%22c2a0dd44-3d59-4b1c-a17f-5f4f1c0e33bf%22%7D; Hm_lvt_9793f42b498361373512340937deb2a0=1670250813,1670720818; __bid_n=184feb8ad3b63e15874207; FEID=v10-7dfe2cb3a52c946f7bbaf83b0d75a37cdf23252d; __xaf_fpstarttimer__=1670720826804; __xaf_ths__={"data":{"0":1,"1":86400,"2":60},"id":"2601b593-4337-4646-99ed-4cf75e8c080c"}; __xaf_thstime__=1670720826807; FPTOKEN=hhpJOar3Pm4F2QF3tBjIjCg9lkLA2F9Mm71+WEXfUwZ1N+pR9tHaRmQnQVmQ2NgQa0bZb2Ntpxuhtqg4Mg0IBjfB07vqq4z/gzR+++P0jmy/02QTrdlsdljF+AU5emWnGGTdobRj0/9YagyuEdK1sY42ZQiSZYCzOjiYxeoCMEaalSB6k2eyTmgIYlGE4IOeq5DjWK4PYfHProC3piqDWRtApvDQzUmJBySmkFyFQgibQ6YgVht01ZpX+1mGa5o2E1I9uASyQZOeUNMHBiRg097fF05z+chXLoTyaC3rxUmCW55cLXooXV1o1rlzDGDw9Br+6eW+Ha3pMBmOS+Xq3aSNqmnldT1z3pQv9YCl0l5dnhqJeeSceqsYElJG9ACppLEYwiQ2EWxmFBI3wo3ZPQ==|UJQeL0Hjl5mZHzA4CKG3sKH7UoEiBkRXqz3XxPbtPcw=|10|195946289e4b100f86abdbadf9399cd6; __xaf_fptokentimer__=1670720826905; Hm_lpvt_9793f42b498361373512340937deb2a0=1670730045'
        item_dict = {}
        for item in cookie.split(";"):
            
            item = item.strip()
            k, v = item.split("=", 1)
            item_dict[k] = v
        yield Request(self.start_urls[0], cookies=item_dict)
        

    def parse(self, response, **kwargs):
        print(response.text)

将cookie信息更新在setting中, 并关闭cookie中间件中的覆盖。则request会携带cookie请求页面

七,案例五 ==> 跳转登录页面进行模拟登录

1- start_spider

from scrapy import cmdline


cmdline.execute("spider crawl novel_17K".split())

2- setting

BOT_NAME = 'mylogin'

SPIDER_MODULES = ['mylogin.spiders']
NEWSPIDER_MODULE = 'mylogin.spiders'


USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36'

ROBOTSTXT_OBEY = False
LOG_LEVEL = "WARNING"

DOWNLOAD_DELAY = 1

3- novel_17k

import scrapy
from scrapy import Request


class Novel17kSpider(scrapy.Spider):
    name = 'novel_17K'
    allowed_domains = ['17k.com']
    start_urls = ['https://user.17k.com/ck/author/shelf?page=1&appKey=2406394919']

    def start_requests(self):
        login_url = 'https://passport.17k.com/ck/user/login'

        params = {
            "loginName": "13059155810",
            "password": "123456"
        }
        """
            params_str = ""
            for k, v in params.items():
                params_str += k+"="+v+"&"
            params_str = params_str[:-1]
        """
        from urllib.parse import urlencode
        params_str = urlencode(params)

        yield Request(login_url, method='POST', body=params_str, callback=self.login_success)

    def login_success(self, response, **kwargs):
        print(response.text)
        print("登录成功")
        yield Request(self.start_urls[0], callback=self.parse)

    def parse(self, response, **kwargs):
        print("进入解析页面")
        print(response.text)
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值