一、需要了解的命令
1、查看帮助
scrapy -h
scrapy <command> -h
- 第一个命令用于查看全部可用命令的帮助信息
- 第二个命令用于查看特定命令的帮助信息
2、全局命令和项目命令
- Project-only必须切到项目文件夹下才能执行
- Global的命令则不需要
Global commands:
startproject #创建项目
genspider #创建爬虫程序
settings #如果是在项目目录下,则得到的是该项目的配置
runspider #运行一个独立的python文件,不必创建项目
shell #scrapy shell url地址 在交互式调试,如选择器规则正确与否
fetch #独立于程单纯地爬取一个页面,可以拿到请求头
view #下载完毕后直接弹出浏览器,以此可以分辨出哪些数据是ajax请求
version #scrapy version 查看scrapy的版本,scrapy version -v查看scrapy依赖库的版本
Project-only commands:
crawl #运行爬虫,必须创建项目才行,确保配置文件中ROBOTSTXT_OBEY = False
check #检测项目中有无语法错误
list #列出项目中所包含的爬虫名
parse #scrapy parse url地址 --callback 回调函数 #以此可以验证我们的回调函数是否正确
bench #scrapy bentch压力测试
(1)全局命令(Global commands)
startproject
:创建一个新的 Scrapy 项目。genspider
:创建一个新的爬虫程序。settings
:显示一个 Scrapy 项目的配置信息。runspider
:运行一个独立的 Python 文件作为爬虫,不需要创建项目。shell
:进入 Scrapy 的交互式调试环境,可以检查选择器规则是否正确。fetch
:单独请求一个页面,并获取响应结果。view
:下载指定页面并在浏览器中打开,用于检查通过哪些请求获取数据。version
:查看当前安装的 Scrapy 版本号。
(2)项目命令(Project-only commands)
crawl
:运行一个 Scrapy 爬虫,必须在项目目录下执行且确保配置文件中的ROBOTSTXT_OBEY
设置为False
。check
:检查项目中是否存在语法错误。list
:列出项目中包含的所有爬虫名称。parse
:使用回调函数解析给定的 URL,用于验证回调函数是否正确。bench
:用于对 Scrapy 进行压力测试。
(3)官网链接
- 如果需要更详细的命令信息,可以参考 Scrapy 官方文档的命令行工具部分
- https://docs.scrapy.org/en/latest/topics/commands.html
二、创建项目
1、创建步骤
(1)创建项目命令
- 通过
Scrapy
命令创建项目
scrapy startproject 项目名
# 例如
scrapy startproject SpiderNewsProjects
(2)进入爬虫项目文件
- 切换到爬虫项目目录
cd SpiderNewsProjects
(3)创建spider项目
- 创建自定义的爬虫程序脚本
scrapy genspider 自定爬虫程序文件名 目标网址
- 示例
scrapy genspider wangyi news.163.com
scrapy genspider huanqiu huanqiu.com
- 创建成功后会在
spiders
文件夹下看到我们创建的自定义的文件名的py文件
2、目录结构
(1)概览
├── NewsPro # 项目名
│ ├── __init__.py
│ ├── items.py # 类似于django的 models表模型,一个个模型类
│ ├── middlewares.py # 中间件
│ ├── pipelines.py # 管道---》写持久化
│ ├── settings.py # 项目配置文件
│ └── spiders # 里面放了自定义的爬虫,类似于app
│ ├── __init__.py
│ ├── huanqiu.py # 自定义爬虫文件
│ └── wangyi.py # 自定义爬虫文件
└── scrapy.cfg # 项目上线配置
(2)说明
-
scrapy.cfg
- 项目的主配置信息,用来部署scrapy时使用,爬虫相关的配置信息在settings.py文件中。
-
items.py
- 设置数据存储模板,用于结构化数据
- 如:Django的Model
-
pipelines
- 数据处理行为
- 如:一般结构化的数据持久化
-
settings.py
- 配置文件
- 如:递归的层数、并发数,延迟下载等。强调:配置文件的选项必须大写否则视为无效,正确写法USER_AGENT='xxxx'
-
spiders
- 爬虫目录
- 如:创建文件,编写爬虫规则
三、示例
1、创建Scrapy项目
#创建一个叫ScrapyDemmo
scrapy startproject ScrapyDemmo
#进入项目文件夹
cd ScrapyDemmo
#创建一个名为baidu的爬虫,爬虫目标www.baidu.com
scrapy genspider baidu www.baidu.com
创建项目完成目录
spiders下的baidu.py是scrapy用命令(scrapy genspider baidu www.baidu.com)自动为我们生成的。
内容如下:
import scrapy
# 自动以当前文件夹命名的类型
class BaiduSpider(scrapy.Spider):
# 爬虫文件的唯一标识
name = 'baidu'
# 允许访问的域名
allowed_domains = ['www.baidu.com']
# 起始 的 URL 列表 (重要)
# 列表内部的 url 都会被框架进行异步请求发送
start_urls = ['http://www.baidu.com/']
# 数据解析主函数:自动调用
# 调用的次数取决于 start_urls 列表内元素的个数
def parse(self, response):
# response : 表示的就是响应对象
pass
在parse方法下我们可以实现
def parse(self, response):
title = response.xpath('//html/dead/title/text()')
print(title)
当然,可以不用命令生成,可以自己在spiders下创建爬虫,您必须继承 scrapy.Spider 类, 且定义以下三个属性:
- name: 用于区别Spider。 该名字必须是唯一的,您不可以为不同的Spider设定相同的名字。
- start_urls: 包含了Spider在启动时进行爬取的url列表。 因此,第一个被获取到的页面将是其中之一。 后续的URL则从初始的URL获取到的数据中提取。
- parse() 是spider的一个方法。 被调用时,每个初始URL完成下载后生成的 Response 对象将会作为唯一的参数传递给该函数。 该方法负责解析返回的数据(response data),提取数据(生成item)以及生成需要进一步处理的URL的 Request 对象。
2、运行爬虫
(1)在项目目录底下用命令运行
如下,我项目目录 D:\PyCharm 2023.2.1\workspace\爬虫\ScrapyDemmo>
,运行name为baidu的爬虫
D:\PyCharm 2023.2.1\workspace\爬虫\ScrapyDemmo>
scrapy crawl baidu
(2)以启动文件启动项目
或者在scrapy项目根目录中,为了避免每一次运行或调试都输入一串命令,可以在项目文件下新建一个run.py文件,每次运行爬虫只需要运行此脚本即可。且运行调试模式也需要设置此启动脚本。
from scrapy import cmdline
cmdline.execute("scrapy crawl baidu".split())
最后运行这个run.py即可,执行结果:
2024-04-05 15:25:32 [scrapy.utils.log] INFO: Scrapy 2.11.1 started (bot: ScrapyDemmo)
2024-04-05 15:25:32 [scrapy.utils.log] INFO: Versions: lxml 5.1.0.0, libxml2 2.10.3, cssselect 1.2.0, parsel 1.9.0, w3lib 2.1.2, Twisted 24.3.0, Python 3.11.1 (tags/v3.11.1:a7a450f, Dec 6 2022, 19:58:39) [MSC v.1934 64 bit (AMD64)], pyOpenSSL 24.1.0 (OpenSSL 3.2.1 30 Jan 2024), cryptography 42.0.5, Platform Windows-10-10.0.22631-SP0
2024-04-05 15:25:32 [scrapy.addons] INFO: Enabled addons:
[]
2024-04-05 15:25:32 [asyncio] DEBUG: Using selector: SelectSelector
2024-04-05 15:25:32 [scrapy.utils.log] DEBUG: Using reactor: twisted.internet.asyncioreactor.AsyncioSelectorReactor
2024-04-05 15:25:32 [scrapy.utils.log] DEBUG: Using asyncio event loop: asyncio.windows_events._WindowsSelectorEventLoop
2024-04-05 15:25:32 [scrapy.extensions.telnet] INFO: Telnet Password: b3e3833546931ae1
2024-04-05 15:25:32 [scrapy.middleware] INFO: Enabled extensions:
['scrapy.extensions.corestats.CoreStats',
'scrapy.extensions.telnet.TelnetConsole',
'scrapy.extensions.logstats.LogStats']
2024-04-05 15:25:32 [scrapy.crawler] INFO: Overridden settings:
{'BOT_NAME': 'ScrapyDemmo',
'FEED_EXPORT_ENCODING': 'utf-8',
'NEWSPIDER_MODULE': 'ScrapyDemmo.spiders',
'REQUEST_FINGERPRINTER_IMPLEMENTATION': '2.7',
'ROBOTSTXT_OBEY': True,
'SPIDER_MODULES': ['ScrapyDemmo.spiders'],
'TWISTED_REACTOR': 'twisted.internet.asyncioreactor.AsyncioSelectorReactor'}
2024-04-05 15:25:33 [scrapy.middleware] INFO: Enabled downloader middlewares:
['scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware',
'scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware',
'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware',
'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware',
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware',
'scrapy.downloadermiddlewares.retry.RetryMiddleware',
'scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware',
'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware',
'scrapy.downloadermiddlewares.redirect.RedirectMiddleware',
'scrapy.downloadermiddlewares.cookies.CookiesMiddleware',
'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware',
'scrapy.downloadermiddlewares.stats.DownloaderStats']
2024-04-05 15:25:34 [scrapy.statscollectors] INFO: Dumping Scrapy stats:
{'downloader/exception_count': 1,
'downloader/exception_type_count/scrapy.exceptions.IgnoreRequest': 1,
'downloader/request_bytes': 224,
'downloader/request_count': 1,
'downloader/request_method_count/GET': 1,
'downloader/response_bytes': 697,
'downloader/response_count': 1,
'downloader/response_status_count/200': 1,
'elapsed_time_seconds': 1.286582,
'finish_reason': 'finished',
'finish_time': datetime.datetime(2024, 4, 5, 7, 25, 34, 785278, tzinfo=datetime.timezone.utc),
'httpcompression/response_bytes': 2814,
'httpcompression/response_count': 1,
'log_count/DEBUG': 7,
'log_count/INFO': 10,
'response_received_count': 1,
'robotstxt/forbidden': 1,
'robotstxt/request_count': 1,
'robotstxt/response_count': 1,
'robotstxt/response_status_count/200': 1,
'scheduler/dequeued': 1,
'scheduler/dequeued/memory': 1,
'scheduler/enqueued': 1,
'scheduler/enqueued/memory': 1,
'start_time': datetime.datetime(2024, 4, 5, 7, 25, 33, 498696, tzinfo=datetime.timezone.utc)}
2024-04-05 15:25:34 [scrapy.core.engine] INFO: Spider closed (finished)
若嫌弃scrapy日志文件太杂乱,想无日志输出,只需在后面增加–nolog即可:
from scrapy import cmdline
cmdline.execute('scrapy crawl baidu --nolog'.split())
(3)持久化存储
执行导出为json或scv格式,执行爬虫文件时添加-o选项即可
scrapy crawl 项目名 -o *.csv
scrapy crawl 项目名 -o *.json
对于json文件,在setting.js文件里添加,设置编码格式,否则会乱码:
from scrapy import cmdline
cmdline.execute('scrapy crawl baidu -o baidu.csv'.split())
四、配置文件
1、默认参数介绍
默认配置文件的参数:
BOT_NAME = 'ScrapyDemmo' # Scrapy项目的名字,这将用来构造默认 User-Agent,同时也用来log,当您使用 startproject 命令创建项目时其也被自动赋值。
SPIDER_MODULES = ['ScrapyDemmo.spiders'] # Scrapy搜索spider的模块列表 默认: [xxx.spiders]
NEWSPIDER_MODULE = 'ScrapyDemmo.spiders' # 使用 genspider 命令创建新spider的模块。默认: 'xxx.spiders'
# 爬取的默认User-Agent,除非被覆盖
# USER_AGENT = 'ScrapyDemmo (+http://www.yourdomain.com)'
# 是否遵循爬虫协议,一般都设为False
ROBOTSTXT_OBEY = True
# Scrapy downloader 并发请求(concurrent requests)的最大值,默认: 16
# CONCURRENT_REQUESTS = 32
# 为同一网站的请求配置延迟(默认值:0)
# See https://docs.scrapy.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs
# DOWNLOAD_DELAY = 3 #下载器在下载同一个网站下一个页面前需要等待的时间,该选项可以用来限制爬取速度,减轻服务器压力。同时也支持小数:0.25 以秒为单位
# 下载延迟设置只有一个有效
# CONCURRENT_REQUESTS_PER_DOMAIN = 16 #对单个网站进行并发请求的最大值。
# CONCURRENT_REQUESTS_PER_IP = 16 #对单个IP进行并发请求的最大值。如果非0,则忽略
# 禁用Cookie(默认情况下启用)
# COOKIES_ENABLED = False
# 禁用Telnet控制台(默认启用)
# TELNETCONSOLE_ENABLED = False
# 覆盖默认请求标头:
# DEFAULT_REQUEST_HEADERS = {
# 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
# 'Accept-Language': 'en',
#}
# 项目管道,300为优先级,越低越爬取的优先度越高
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
# 持久化配置
# ITEM_PIPELINES = {
# 'ScrapyDemmo.pipelines.ScrapydemmoPipeline': 300,
#}
2、额外设置
还可以设置日志的等级与日志存放的路径:
相关变量:
# 日志级别设置
LOG_LEVEL= ""
LOG_FILE="日志名.log"
日志等级分为,默认等级是1
- DEBUG 调试信息
- INFO 一般信息
- WARNING 警告
- ERROR 普通错误
- CRITICAL 严重错误
如果设置LOG_LEVEL=“WARNING”,就只会WARNING等级之下的ERROR和CRITICAL
一般主要需要配置的几个参数,其他按需配置即可。
- USER_AGENT:默认是注释的,这个东西非常重要,如果不写很容易被判断为电脑爬虫。
- ROBOTSTXT_OBEY:是否遵循机器人协议,默认是true,需要改为false,否则很多东西爬不了
- DEFAULT_REQUEST_HEADERS:覆盖默认请求头,和USER_AGENT类似,只是参数更完整。
# 1.增加并发数,默认为16,可以根据需求进行调整
# 默认scrapy开启的并发线程为32个,可以适当进行增加。
# 值为100,并发设置成了为100。
# 在settings配置文件中修改
CONCURRENT_REQUESTS = 100
# 2.降低日志级别,可设置为INFO或ERROR,减少日志输出,提高性能
# 在运行scrapy时,会有大量日志信息的输出,为了减少CPU的使用率。
# 可以设置log输出信息为INFO或者ERROR即可。
在配置文件中编写:
LOG_LEVEL = 'INFO'
# 3.禁止使用Cookie,默认为True,如果不需要使用Cookie可以设置为False
# 如果不是真的需要cookie,则在scrapy爬取数据时可以禁止cookie从而减少CPU的使用率,提升爬取效率。
COOKIES_ENABLED = False
# 4.禁止重试,默认为True,如果不需要进行重试请求可以设置为False
# 对失败的HTTP进行重新请求(重试)会减慢爬取速度,因此可以禁止重试。
RETRY_ENABLED = False
# 5.设置下载超时时间,默认180秒,可以根据需求进行调整
# 如果对一个非常慢的链接进行爬取,减少下载超时可以能让卡住的链接快速被放弃,从而提升效率。
# 超时时间为10s
DOWNLOAD_TIMEOUT = 10
五、实现数据解析
1、CSS解析器
(1)概览
response.css(‘a’)
返回的是selector对象,response.css(‘a’).extract()
返回的是a标签对象response.css(‘a::text’).extract_first()
返回的是第一个a标签中文本的值response.css(‘a::attr(href)’).extract_first()
返回的是第一个a标签中href属性的值response.css(‘a[href*=image]::attr(href)’).extract()
返回所有a标签中href属性包含image的值response.css(‘a[href*=image] img::attr(src)’).extract()
返回所有a标签下image标签的src属性
(2)基本语法
语法 | 说明 |
---|---|
* | 选择所有节点 |
#container | 选择id为container的节点 |
.container | 选择所有class包含container的节点 |
div,p | 选择所有 div 元素和 所有 p 元素 |
li a | 选取所有li 下所有a节点 |
ul + p | 选取ul后面的第一个p元素 |
div#container > ul | 选取id为container的div的第一个ul子元素 |
ul ~p | 选取与ul相邻的所有p元素 |
a[title] | 选取所有有title属性的a元素 |
a[href=”http://baidu.com”] | 选取所有href属性为http://baidu.com的a元素 |
a[href*=”baidu”] | 选取所有href属性值中包含baidu的a元素 |
a[href^=”http”] | 选取所有href属性值中以http开头的a元素 |
a[href$=”.jpg”] | 选取所有href属性值中以.jpg结尾的a元素 |
input[type=radio]:checked | 选择选中的radio的元素 |
div:not(#container) | 选取所有id为非container 的div属性 |
li:nth-child(3) | 选取第三个li元素 |
li:nth-child(2n) | 选取第偶数个li元素 |
a::attr(href) | 选取a标签的href属性 |
a::text | 选取a标签下的文本 |
(3)更多语法
选择器
是一种模式,用于选择需要添加样式的元素。CSS列
指示该属性是在哪个 CSS 版本中定义的。(CSS1、CSS2 还是 CSS3)
选择器 | 例子 | 例子描述 | CSS |
---|---|---|---|
.class | .intro | 选择 class=”intro” 的所有元素。 | 1 |
#id | #firstname | 选择 id=”firstname” 的所有元素。 | 1 |
* | * | 选择所有元素。 | 2 |
element | p | 选择所有 <p> 元素。 |
1 |
element,element | div,p | 选择所有<div> 元素和所有<p> 元素。 |
1 |
element element] | div p | 选择<div> 元素内部的所有<p> 元素。 |
1 |
element>element | div>p | 选择父元素为 <div> 元素的所有<p> 元素。 |
2 |
element+element | div+p | 选择紧接在<div> 元素之后的所有 <p> 元素。 |
2 |
[attribute] | [target] | 选择带有 target 属性所有元素。 | 2 |
[attribute=value] | [target=_blank] | 选择 target=”_blank” 的所有元素。 | 2 |
[attribute~=value] | [title~=flower] | 选择 title 属性包含单词 “flower” 的所有元素。 | 2 |
[attribute|=value] | [lang|=en] | 选择 lang 属性值以 “en” 开头的所有元素。 | 2 |
:link | a:link | 选择所有未被访问的链接。 | 1 |
:visited | a:visited | 选择所有已被访问的链接。 | 1 |
:active | a:active | 选择活动链接。 | 1 |
:hover | a:hover | 选择鼠标指针位于其上的链接。 | 1 |
:focus | input:focus | 选择获得焦点的 input 元素。 | 2 |
:first-letter | p:first-letter | 选择每个 <p> 元素的首字母。 |
1 |
:first-line | p:first-line | 选择每个 <p> 元素的首行。 |
1 |
:first-child | p:first-child | 选择属于父元素的第一个子元素的每个 <p> 元素。 |
2 |
:before | p:before | 在每个 <p> 元素的内容之前插入内容。 |
2 |
:after | p:after | 在每个<p> 元素的内容之后插入内容。 |
2 |
:lang(language) | p:lang(it) | 选择带有以 “it” 开头的 lang 属性值的每个<p> 元素。 |
2 |
element1~element2 | p~ul | 选择前面有 <p> 元素的每个 <ul> 元素。 |
3 |
[attribute^=value] | a[src^=”https”] | 选择其 src 属性值以 “https” 开头的每个 <a> 元素。 |
3 |
[attribute$=value] | a[src$=”.pdf”] | 选择其 src 属性以 “.pdf” 结尾的所有 <a> 元素。 |
3 |
[attribute*=value] | a[src*=”abc”] | 选择其 src 属性中包含 “abc” 子串的每个 <a> 元素。 |
3 |
:first-of-type | p:first-of-type | 选择属于其父元素的首个 <p> 元素的每个 <p> 元素。 |
3 |
:last-of-type | p:last-of-type | 选择属于其父元素的最后 <p> 元素的每个 <p> 元素。 |
3 |
:only-of-type | p:only-of-type | 选择属于其父元素唯一的 <p> 元素的每个 <p> 元素。 |
3 |
:only-child | p:only-child | 选择属于其父元素的唯一子元素的每个 <p> 元素。 |
3 |
:nth-child(n) | p:nth-child(2) | 选择属于其父元素的第二个子元素的每个 <p> 元素。 |
3 |
:nth-last-child(n) | p:nth-last-child(2) | 同上,从最后一个子元素开始计数。 | 3 |
:nth-of-type(n) | p:nth-of-type(2) | 选择属于其父元素第二个 <p> 元素的每个 <p> 元素。 |
3 |
:nth-last-of-type(n) | p:nth-last-of-type(2) | 同上,但是从最后一个子元素开始计数。 | 3 |
:last-child | p:last-child | 选择属于其父元素最后一个子元素每个 <p> 元素。 |
3 |
:root | :root | 选择文档的根元素。 | 3 |
:empty | p:empty | 选择没有子元素的每个 <p> 元素(包括文本节点)。 |
3 |
:target | #news:target | 选择当前活动的 #news 元素。 | 3 |
:enabled | input:enabled | 选择每个启用的 <input> 元素。 |
3 |
:disabled | input:disabled | 选择每个禁用的 <input> 元素 |
3 |
:checked | input:checked | 选择每个被选中的 <input> 元素。 |
3 |
:not(selector) | :not(p) | 选择非 <p> 元素的每个元素。 |
3 |
::selection | ::selection | 选择被用户选取的元素部分。 | 3 |
2、Xpath解析器
(1)概览
- Scrapy xpath语法,Xpath是XML Path的简介,基于XML树状结构,可以在整个树中寻找锁定目标节点。由于HTML文档本身就是一个标准的XML页面,因此我们可以使用XPath的语法来定位页面元素。
(2)路径表达式
表达式 | 描述 | 实例 |
---|---|---|
nodename | 选取nodename节点的所有子节点 | //div |
/ | 从根节点选取 | /div |
// | 选取任意位置的节点,不考虑他们的位置 | //div |
. | 选取当前节点 | ./div |
.. | 选取当前节点的父节点 | .. |
@ | 选取属性 | //@calss |
- 示例
语法 | 说明 |
---|---|
artical | 选取所有artical元素的子节点 |
/artical | 选取根元素artical |
./artical | 选取当前元素下的artical |
../artical | 选取父元素下的artical |
artical/a | 选取所有属于artical的子元素a元素 |
//div | 选取所有div 子元素,无论div在任何地方 |
artical//div | 选取所有属于artical的div 元素,无论div元素在artical的任何位置 |
//@class | 选取所有class属性 |
a/@href | 选取a标签的href属性 |
a/text() | 选取a标签下的文本 |
string(.) | 解析出当前节点下所有文字 |
string(..) | 解析出父节点下所有文字 |
(3)谓语
- 谓语被嵌在方括号内,用来查找某个特定的节点或包含某个制定的值的节点
语法 | 说明 |
---|---|
/artical/div[1] | 选取所有属于artical 子元素的第一个div元素 |
/artical/div[last()] | 选取所有属于artical子元素的最后一个div元素 |
/artical/div[last()-1] | 选取所有属于artical子元素的倒数第2个div元素 |
/artical/div[position()❤️] | 选取所有属于artical子元素的前2个div元素 |
//div[@class] | 选取所有拥有属性为class的div节点 |
//div[@class=”main”] | 选取所有div下class属性为main的div节点 |
//div[price>3.5] | 选取所有div下元素值price大于3.5的div节点 |
(4)通配符
- Xpath通过通配符来选取未知的XML元素
表达式 | 结果 |
---|---|
//* | 选取所有元素 |
//div/* | 选取所有属于div元素的所有子节点 |
//div[@*] | 选取所有带属性的元素 |
(5) 取多个路径
- 使用
|
运算符可以选取多个路径
表达式 | 结果 |
---|---|
//div | //table | 选取文档中所有的div和table节点 |
//div/a | //div/p | 选取所有div元素的a和p 元素 |
artical/div/pl | //span | 选取所有div下的pl和文档中所有span |
(6)轴
- 轴可以定义相对于当前节点的节点集
轴名称 | 表达式 | 描述 |
---|---|---|
ancestor | ./ancestor:
标签:基本,框架,元素,爬虫,选取,scrapy,div,节点
From: https://www.cnblogs.com/xiao01/p/18116250
相关文章
|