jjjzw 发表于 2022-6-23 19:58

【笔记】Scrapy爬虫学习笔记(一)

之前一直使用requests和beautifulsoup来进行爬虫和脚本。最近毕业找工作,看了看爬虫工程师的岗位,发现有些要求熟悉scrapy和pyspider的框架,于是打算入坑学习一下,更新一下我的战术储备。
这两天看了不少文档和教程,略微了解了这个框架,现在将我学习的笔记分享一下,希望能有用处!

# Scrapy学习笔记

## 什么是Scrapy?

> Scrapy是一个适用爬取网站数据、提取结构性数据的应用程序框架,它可以应用在广泛领域:Scrapy 常应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。通常我们可以很简单的通过 Scrapy 框架实现一个爬虫,抓取指定网站的内容或图片。

结构:

![结构](https://pic.rmb.bdstatic.com/bjh/down/d28e83db92e0e94adc238cb337d073c5.jpeg)

+ **Scrapy Engine(引擎)**:负责Spider、ItemPipeline、Downloader、Scheduler中间的通讯,信号、数据传递等。

+ **Scheduler(调度器)**:它负责接受引擎发送过来的Request请求,并按照一定的方式进行整理排列,入队,当引擎需要时,交还给引擎。

+ **Downloader(下载器)**:负责下载Scrapy Engine(引擎)发送的所有Requests请求,并将其获取到的Responses交还给Scrapy Engine(引擎),由引擎交给Spider来处理。

+ **Spiders(爬虫)**:它负责处理所有Responses,从中分析提取数据,获取Item字段需要的数据,并将需要跟进的URL提交给引擎,再次进入Scheduler(调度器)。

+ **Item Pipeline(管道)**:它负责处理Spider中获取到的Item,并进行进行后期处理(详细分析、过滤、存储等)的地方。

+ **Downloader Middlewares(下载中间件)**:一个可以自定义扩展下载功能的组件。

+ **Spider Middlewares(Spider中间件)**:一个可以自定扩展和操作引擎和Spider中间通信的功能组件。



**总结**:一个功能强大的爬虫框架:)接下来学习如何使用scrapy

## 安装

```bash
pip install Scrapy
```



## 一、创建项目

在终端输入:

```bash
scrapy startproject mySpider
```

生成的项目文件结构为:

```txt
mySpider/
    scrapy.cfg
    mySpider/
      __init__.py
      items.py
      pipelines.py
      settings.py
      spiders/
            __init__.py
            ...
```

其中,

- scrapy.cfg为项目的配置文件。
- mySpider/为项目的Python模块,将会从这里引用代码。
- mySpider/items.py为项目的目标文件。
- mySpider/pipelines.py为项目的管道文件。
- mySpider/settings.py为项目的设置文件。
- mySpider/spiders/为存储爬虫代码目录。



## 二、创建蜘蛛

创建项目后,我们需要创建蜘蛛来爬取内容

蜘蛛的代码储存在`mySpider/spiders/`目录下

可以自行在文件夹中创建蜘蛛文件,也可以通过命令行自动完成创建

```bash
cd mySpider# 进入根目录
scrapy genspider biquge "xbiquwx.la"# biquge为蜘蛛名称,"xbiquwx.la"为目标url
```

创建完成后,在`mySpider/spiders/`目录下生成`biquge.py`文件,其内容为:

```python
import scrapy


class BiqugeSpider(scrapy.Spider):
    name = 'biquge'# 名称
    allowed_domains = ['xbiquwx.la']# 允许运行的域
    start_urls = ['http://xbiquwx.la/']# urls,该列表下的url将会依次进行请求

    def parse(self, response):# 回调函数
      pass
```

其中,`parse()`函数用于在每次迭代请求后处理响应



> 例1:对小说某章节进行内容提取,并写入txt
>
> ```python
> class BiqugeSpider(scrapy.Spider):
>   name = 'biquge'
>   allowed_domains = ['xbiquwx.la']
>   start_urls = ['https://www.xbiquwx.la/26_26516/10289395.html']
>
>   def parse(self, response):# 回调函数
>         content = response.xpath('//*[@id="content"]').extract_first()# 通过xpath提取内容
>         with open("result.txt", "w") as f:
>             f.write(content)
> ```



## 三、运行蜘蛛

在根目录下运行:

```bash
scrapy crawl biquge
```

运行`例1`中的蜘蛛,在根目录下得到`result.txt`



## 四、数据提取

### 1、控制台提取

使用`shell`来调试数据

```bash
scrapy shell "https://www.xbiquwx.la/26_26516/10289395.html"
```

`shell`将请求该页面的代码,并开始调试

通过`response.css()`的方法来定位元素,并返回一个或多个选择器



> 例2:一些定位样式:
> ```bash
> >>> response.css("div.bottom")# 选择div标签下class="bottom"的元素
> >>> response.css(".bottom")# 选择所有class="bottom"的元素
> >>> response.css("#content")# 选择所有id="bottom"的元素
> ```



scrapy将返回一个列表用于储存选择器

```bash
>>> title = response.css("title")
>>> title_content = title.css("title::text").get()# '::'符号用于提取该元素下的特定属性,如'text''href'等
>>> title_content = title.css("::text").get()# 对于本元素下的属性,可以省略本元素标签
>>> title_content = title.css("br,::text").getall()# 选取多个元素,用','隔开,使用getall()方法提取
```

更简略的是,对于简单的元素,可以将上面的步骤合并起来:

```bash
>>> title = response.css("title::text").get()
>>> content = response.css("div#content").get()
```



### 2、代码提取

将上面的提取过程放在回调函数中:

```python
import scrapy


class BiqugeSpider(scrapy.Spider):
    name = 'biquge'# 名称
    allowed_domains = ['xbiquwx.la']# 允许运行的域
    start_urls = ['https://www.xbiquwx.la/26_26516/10289395.html']# urls,该列表下的url将会依次进行请求

    def parse(self, response):# 回调函数
      for box in response.css('div.box_con'):
            yield {
                'content': box.css('div#content::text').getall(),
            }
```

运行蜘蛛并将结果保存至json文件中:

```bash
scrapy crawl biquge -O result.json# 可以使用json、xml、csv、jl(json line)
```



## 五、管道处理

管道用于蜘蛛提取数据后,进行的数据处理和下载任务

管道定义在`piplines.py`文件中

```python
from itemadapter import ItemAdapter


class MyspiderPipeline:
          def __init__(self):# 管道初始化时的方法
      pass

    def process_item(self, item, spider):# 管道处理数据的方法(necessary),输入参数为item
      return item# 处理完毕后返回引擎 (necessary)

    def open_spider(self, spider):# 蜘蛛开启时的方法
      pass

    def close_spider(self, spider):# 管道关闭时的方法
            pass
```



## 附录 CSS选择器

| 选择器               | 示例                  | 示例说明                                                |
| :--------------------- | --------------------- | --------------------------------------------------------- |
| class                  | .intro                | 选择所有class="intro"的元素                               |
| #id                  | #firstname            | 选择所有id="firstname"的元素                              |
| *                      | *                     | 选择所有元素                                              |
| element                | p                     | 选择所有<p>元素                                           |
| element,element      | div,p               | 选择所有<div>元素和<p>元素                              |
| elementelement       | div p               | 选择<div>元素内的所有<p>元素                              |
| element>element      | div>p               | 选择所有父级是 <div> 元素的 <p> 元素                      |
| element+element      | div+p               | 选择所有紧接着<div>元素之后的<p>元素                      |
|             |             | 选择所有带有target属性元素                              |
|       |        | 选择所有使用target="-blank"的元素                         |
|    |        | 选择标题属性包含单词"flower"的所有元素                  |
| |          | 选择一个lang属性的起始值="EN"的所有元素                   |
| :link                  | a:link                | 选择所有未访问链接                                        |
| :visited               | a:visited             | 选择所有访问过的链接                                    |
| :active                | a:active            | 选择活动链接                                              |
| :hover               | a:hover               | 选择鼠标在链接上面时                                    |
| :focus               | input:focus         | 选择具有焦点的输入元素                                    |
| :first-letter          | p:first-letter      | 选择每一个<P>元素的第一个字母                           |
| :first-line            | p:first-line          | 选择每一个<P>元素的第一行                                 |
| :first-child         | p:first-child         | 指定只有当<p>元素是其父级的第一个子级的样式。             |
| :before                | p:before            | 在每个<p>元素之前插入内容                                 |
| :after               | p:after               | 在每个<p>元素之后插入内容                                 |
| :lang(language)      | p:lang(it)            | 选择一个lang属性的起始值="it"的所有<p>元素                |
| element1~element2      | p~ul                  | 选择p元素之后的每一个ul元素                               |
|    | a       | 选择每一个src属性的值以"https"开头的元素                  |
|    | a      | 选择每一个src属性的值以".pdf"结尾的元素                   |
|    | a      | 选择每一个src属性的值包含子字符串"runoob"的元素         |
| :first-of-type         | p:first-of-type       | 选择每个p元素是其父级的第一个p元素                        |
| :last-of-type          | p:last-of-type      | 选择每个p元素是其父级的最后一个p元素                      |
| :only-of-type          | p:only-of-type      | 选择每个p元素是其父级的唯一p元素                        |
| :only-child            | p:only-child          | 选择每个p元素是其父级的唯一子元素                         |
| :nth-child(n)          | p:nth-child(2)      | 选择每个p元素是其父级的第二个子元素                     |
| :nth-last-child(n)   | p:nth-last-child(2)   | 选择每个p元素的是其父级的第二个子元素,从最后一个子项计数 |
| :nth-of-type(n)      | p:nth-of-type(2)      | 选择每个p元素是其父级的第二个p元素                        |
| :nth-last-of-type(n)   | p:nth-last-of-type(2) | 选择每个p元素的是其父级的第二个p元素,从最后一个子项计数|
| :last-child            | p:last-child          | 选择每个p元素是其父级的最后一个子级。                     |
| :root                  | :root               | 选择文档的根元素                                          |
| :empty               | p:empty               | 选择每个没有任何子级的p元素(包括文本节点)               |
| :target                | #news:target          | 选择当前活动的#news元素(包含该锚名称的点击的URL)      |
| :enabled               | input:enabled         | 选择每一个已启用的输入元素                              |
| :disabled            | input:disabled      | 选择每一个禁用的输入元素                                  |
| :checked               | input:checked         | 选择每个选中的输入元素                                    |
| :not(selector)         | :not(p)               | 选择每个并非p元素的元素                                 |
| ::selection            | ::selection         | 匹配元素中被用户选中或处于高亮状态的部分                  |
| :out-of-range          | :out-of-range         | 匹配值在指定区间之外的input元素                           |
| :in-range            | :in-range             | 匹配值在指定区间之内的input元素                           |
| :read-write            | :read-write         | 用于匹配可读及可写的元素                                  |
| :read-only             | :read-only            | 用于匹配设置 "readonly"(只读) 属性的元素                |
| :optional            | :optional             | 用于匹配可选的输入元素                                    |
| :required            | :required             | 用于匹配设置了 "required" 属性的元素                      |
| :valid               | :valid                | 用于匹配输入值为合法的元素                              |
| :invalid               | :invalid            | 用于匹配输入值为非法的元素                              |

bryanhan 发表于 2022-6-23 20:14

爬虫还是不要干了,自己玩还行,不要干这个工作,几年之后会后悔的,还是学其他的吧

jjjzw 发表于 2022-6-23 20:31

bryanhan 发表于 2022-6-23 20:14
爬虫还是不要干了,自己玩还行,不要干这个工作,几年之后会后悔的,还是学其他的吧

谢谢指点!我今年打算考研,然后随便找个工作过度一下{:301_1008:}

wanderrr 发表于 2022-6-24 09:26

bryanhan 发表于 2022-6-23 20:14
爬虫还是不要干了,自己玩还行,不要干这个工作,几年之后会后悔的,还是学其他的吧

为什么几年后会后悔啊?

Mc小8 发表于 2022-6-24 11:26

wanderrr 发表于 2022-6-24 09:26
为什么几年后会后悔啊?

官方赠与银手镯一副,质保10年{:1_918:}

pangpang02 发表于 2022-6-24 13:55

加油,有时候用得上的

swj330702 发表于 2022-6-24 15:31

有时候用得上的

wycdd 发表于 2022-6-24 20:32

学习了,有空练练
页: [1]
查看完整版本: 【笔记】Scrapy爬虫学习笔记(一)