Python爬虫之Scrapy框架的使用(一)

本文深入讲解Scrapy框架的模块功能、工作流程及安装方法,演示如何创建项目、爬虫,定义爬取字段,配置settings,使用logging,以及在PyCharm中调试。此外,文章还介绍了如何使用pipeline保存数据,包括JSON文件保存方式、异步数据存储到MySQL,以及发送POST请求的方法。
部署运行你感兴趣的模型镜像

一:scrapy框架介绍

Scrapy官方文档:

https://docs.scrapy.org/en/latest/intro/tutorial.html

写一个爬虫,需要做很多的事情。比如:发送网络请求、数据解析、数据存储、反反爬虫机制(更换ip代理、设置请求头等)、异步请求等。这些工作如果每次都要自己从零开始写的话,比较浪费时间。因此Scrapy把一些基础的东西封装好了,在他上面写爬虫可以变的更加的高效(爬取效率和开发效率)。因此真正在公司里,一些上了量的爬虫,都是使用Scrapy框架来解决。
Scrapy使用Twisted异步网络框架,可以加快下载速度。

异步与非阻塞的区别:
异步与非阻塞的区别

1.1 Scrapy框架模块功能

Scrapy Engine(引擎):Scrapy框架的核心部分。负责在Spider和ItemPipeline、Downloader、Scheduler中间通信、传递数据等。
Spider(爬虫):发送需要爬取的链接给引擎,最后引擎把其他模块请求回来的数据再发送给爬虫,爬虫就去解析想要的数据。这个部分是我们开发者自己写的,因为要爬取哪些链接,页面中的哪些数据是我们需要的,都是由程序员自己决定。
Scheduler(调度器):负责接收引擎发送过来的请求,并按照一定的方式进行排列和整理,负责调度请求的顺序等。
Downloader(下载器):负责接收引擎传过来的下载请求,然后去网络上下载对应的数据再交还给引擎。
Item Pipeline(管道):负责将Spider(爬虫)传递过来的数据进行保存。具体保存在哪里,应该看开发者自己的需求。
Downloader Middlewares(下载中间件):可以扩展下载器和引擎之间通信功能的中间件。
Spider Middlewares(Spider中间件):可以扩展引擎和爬虫之间通信功能的中间件。

1.2 Scrapy爬虫流程

Scrapy爬虫流程图

1.3 安装scrapy

# Scrapy官方文档:
http://doc.scrapy.org/en/latest
# Scrapy中文文档:
http://scrapy-chs.readthedocs.io/zh_CN/latest/index.html

Linux系统上安装

# Ubuntu16.04上安装
apt-get install python3-dev build-essential python3-pip libxml2-dev libxslt1-dev zlib1g-dev libffi-dev libssl-dev
# 如果是在虚拟环境中安装上面的命令不执行,直接执行下面的命令即可
pip install scrapy

Win系统上安装

# win上虚拟环境中安装
# 以下两个模块都要安装
pip install scrapy
# 如果在windows系统下,提示这个错误ModuleNotFoundError: No module named 'win32api',那么使用以下命令可以解决
pip install pypiwin32

如果是在Win系统上安装出错,可到下面的地址下载scrapy,Twisted 两个程序。注意选择对应的版本。

https://www.lfd.uci.edu/~gohlke/pythonlibs/

然后在scrapy程序下载的目录中打开命令窗口,使用以下命令进行安装

pip install -i https://pypi.douban.com/simple Twisted‑20.3.0‑cp37‑cp37m‑win_amd64.whl	# cp37指的是win上安装的python版本为3.7
pip install -i https://pypi.douban.com/simple Scrapy-2.1.0-py3-none-any.whl
# Scrapy所需要的依赖会从douban源进行下载

二:scrapy框架的使用

2.1 创建scrapy项目

要使用Scrapy框架创建项目,需要通过命令来创建。首先进入到你想把这个项目存放的目录。然后使用以下命令创建:

# scrapy startproject [项目名称]
scrapy startproject qiushi

目录结构介绍:

# 以下介绍下主要文件的作用
items.py:用来存放爬虫爬取下来数据的模型。
middlewares.py:用来存放各种中间件的文件。
pipelines.py:用来将items的模型存储到本地磁盘中。
settings.py:本爬虫的一些配置信息(比如请求头、多久发送一次请求、ip代理池等)。
scrapy.cfg:项目的配置文件。
spiders包:以后所有的爬虫,都是存放到这个里面。

2.2 创建爬虫

然后进入到项目目录,创建一个爬虫:

# scrapy genspider 爬虫名 域名
cd qiushi
scrapy genspider qs qiushibaike.com
# 创建了一个名字叫做qs的爬虫,并且能爬取的网页只会限制在qiushibaike.com这个域名下

默认会在spiders目录下自动创建一个名为qs.py的文件,在该文件下可编写要爬取的url数据:

import scrapy
from qiushi.items import QiushiItem
class QsSpider(scrapy.Spider):
    name = 'qs'
    allowed_domains = ['qiushibaike.com']
    start_urls = ['https://www.qiushibaike.com/text/page/1/']

    def parse(self, response):
        content = response.xpath('//div[@class="col1 old-style-col1"]/div') # 获取前面div下的所有子div元素
        for con in content:
            author = con.xpath('.//h2/text()').extract_first()
            contents = con.xpath('.//div[@class="content"]/span/text()').extract_first()
            item = QiushiItem(author = author, content = contents)
            yield item
        # 获取下一页url
        next_url = response.xpath('//ul[@class="pagination"]/li[last()]/a/@href').extract_first()
        if not next_url: return
        yield scrapy.Request(parse.urljoin(response.url, next_url), callback = self.parse)

2.3 items中定义要爬取的字段

# 在items.py文件中定义需要的字段
import scrapy
class QiushiItem(scrapy.Item):
    author = scrapy.Field()
    article = scrapy.Field()

2.4 settings配置文件配置

在做一个爬虫之前,一定要记得修改setttings.py中的设置。两个地方是强烈建议设置的。
ROBOTSTXT_OBEY设置为False。默认是True,即遵守机器协议,那么在爬虫的时候,scrapy首先去找robots.txt文件,如果没有找到。则直接停止爬取。
DEFAULT_REQUEST_HEADERS添加User-Agent。这个也是告诉服务器,我这个请求是一个正常的请求,不是一个爬虫。

ROBOTSTXT_OBEY = False
DOWNLOAD_DELAY = 1
DEFAULT_REQUEST_HEADERS = {
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Language': 'en',
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11'	# 添加此行
}
# 需要用到pipelines时在settings配置文件中取消下面三行的注释
ITEM_PIPELINES = {
   'myspider.pipelines.MyspiderPipeline': 300,
   # 值300表示距离引擎的远近(权重)。值越小表示距离引擎越近。
   # 在scrapy中可以定义多个pipelines,该值表示数据经过离引擎的顺序,先经过离引擎最近的pipelines
}
############ 以下是增加的配置项 #################
LOG_LEVEL = "WARNING"   # 控制台只输出WARNING及以上等级的日志

2.5 loggin模块的使用

在settings配置文件中配置日志选项:

############ 以下是增加的配置项 #################
LOG_LEVEL = "WARNING"   # 控制台只输出WARNING及以上等级的日志
LOG_FILE = './log.log'  # 指定log输入的文件路径(有此项配置日志不会在控制台输出,只会输入到文件)
# 日志输出到文件在开发阶段可注释

使用方法如下:

import scrapy
import logging
logger = logging.getLogger(__name__)	# 实例化loggin,传入__name__参数会输出当前日志是由哪个文件输出的
class ItcastSpider(scrapy.Spider):
    name = 'itcast'  # 爬虫名
    allowed_domains = ['itcast.cn']  # 允许爬取的范围
    start_urls = ['http://www.itcast.cn/channel/teacher.shtml']  # 最开始请求的URL地址
    def parse(self, response):  # 这里的函数名必须为parse
        # 处理start_url地址对应的响应
        # result = response.xpath("//div[@class='li_txt']//h30/text()").extract()
        # # extract()没有取到数据返回一个空列表
        # print(result)
        li_list = response.xpath("//div[@class='tea_con']//li")[:5]
        # print(li_list)
        for li in li_list:
            item = {}
            item['name'] = li.xpath(".//h3/text()").extract_first()
            # extract_first()取不到数据返回None
            item['title'] = li.xpath(".//h4/text()").extract_first()
            # print(item)
            logger.warning(item)	# 这里使用warning是因为在settings配置文件中配置了warning
            # yield item  # 将数据传递给pipeline处理

日志输出结果如下:
python loggin模块的使用

2.6 pycharm中调试scrapy

在命令行中执行命令运行scrapy不方便代码的调试

scrapy crawl qs 	# qs为spider名

在项目目录下新建start.py文件(文件名随意),内容如下:

from scrapy.cmdline import execute
import sys, os
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
execute(['scrapy', 'crawl', 'qs'])

然后可以运行start.py文件即可运行scrapy程序,或者debug调试模式启动

2.7 pipeline保存数据

使用pipeline的前提是在settings配置文件中启用pipeline:

# 取消注释以下几行
ITEM_PIPELINES = {
   'qiushi.pipelines.QiushiPipeline': 300,
}

1:保存为Json文件

import json
class QiushiPipeline:
    '''该类中有三个方法会常用到:open_spider, close_spider,process_item'''
    def __init__(self):
        self.fp = open('duanzi.json', 'w', encoding='utf-8')
    def open_spider(self, spider):
    	# 当爬虫打开时被调用
        print('start...')
    def process_item(self, item, spider):
    	# 当爬虫有item传过来时被调用,传递过来的item是一个<class 'qiushi.items.QiushiItem'>对象,所以下面需要将item转换成字典才能被正常序列化
        self.fp.write(json.dumps(dict(item), ensure_ascii=False) + '\n')
        return item
    def close_spider(self, spider):
    	# 当爬虫关闭时被调用
        self.fp.close()
        print('finish...')

保存的文件内容如下图:
Python爬虫之Scrapy框架的使用
2:使用JsonItemExporter保存Json文件

from scrapy.exporters import JsonItemExporter
class QiushiPipeline:
    '''该类中默认会执行三个方法:open_spider, close_spider,process_item'''
    def __init__(self):
        self.fp = open('duanzi.json', 'wb')
        self.exporter = JsonItemExporter(self.fp, ensure_ascii = False, encoding = 'utf-8')
        self.exporter.start_exporting()
    def open_spider(self, spider):
        print('start...')
    def process_item(self, item, spider):
        self.exporter.export_item(item)
        return item
    def close_spider(self, spider):
        self.exporter.finish_exporting()
        self.fp.close()
        print('finish...')
# 最后保存的文件为一个列表中多个字典数据        

3:使用JsonLinesItemExporter保存Json文件

from scrapy.exporters import JsonLinesItemExporter
class QiushiPipeline:
    '''该类中默认会执行三个方法:open_spider, close_spider,process_item'''
    def __init__(self):
        self.fp = open('duanzi.json', 'wb')
        self.exporter = JsonLinesItemExporter(self.fp, ensure_ascii = False, encoding = 'utf-8')
    def open_spider(self, spider):
        print('start...')
    def process_item(self, item, spider):
        self.exporter.export_item(item)
        return item
    def close_spider(self, spider):
        self.fp.close()
        print('finish...')
# 最后保存到文件中的数据就是一个个的字典        

JsonItemExporterJsonLinesItemExporter方法都要用二进制的方式来写
首先我们从名字里大致可以看出来了,两者区别 Lines 也就是行的意思。也就是说 前者是一起写进json文件里,后者是我们每次parse函数yield的item,经过处理就直接写入json里面。那么我们可以想一下,前者,既然是一起写入,假如我们爬取的的数据有上万条,那就很吃内存了。后者是一条一条的存,对内存很友好,他每次写入的是一个 dict,但读取的时候,没有前者友好。

2.8 爬虫面试题

面试题:将爬到的数据一份存储到本地,一份存储到数据库,如何实现?
解答:

  1. 管道文件中一个管道类对应的是将数据存在到一种平台
  2. 爬虫文件提交的item只会给管道文件中优先级最高的管道类
  3. process_item中的return item表示将item传递给下一个即将被执行的管道类

案例如下:

import pymysql
class QuishiPipeline:
    fp = None
    # 重写父类的一个方法:该方法只在开始爬虫时被调用一次
    def open_spider(self, spider):
        print('开始爬虫...')
        self.fp = open('./qiubai.txt', 'w', encoding='utf-8')
    # 该方法可以接收爬虫文件提交过来的item对象
    # 该方法没接收到一个item对象就会被调用一次
    def process_item(self, item, spider):
        author = item['author']
        content = item['content']
        self.fp.write(author + ':' + content + '\n')
        return item # 这里会将item传递给下一个管道类。不管有没有下一个管道类,都要将item return
    def close_spider(self, spider):
        print('结束爬虫...')
        self.fp.close()
# 管道文件中一个管道类对应将一组数据存储到一个平台或载体中
# 爬虫文件提交的item类型的对象,最终会提交给哪一个管道类呢?给的一定是优先级高的那个管道
class MysqlPipeline:
    conn = None
    cursor = None
    def open_spider(self, spider):
        self.conn = pymysql.Connect(host='172.17.2.36', port=3306, user='root', password='xxxxxxx', db='spider', charset = 'utf8')
    def process_item(self, item, spider):
        self.cursor = self.conn.cursor()
        try:
            self.cursor.execute('insert into qiubai values(null, "%s", "%s")' % (item["author"], item["content"]))
            self.conn.commit()
        except Exception as e:
            print(e)
            self.conn.rollback()
        return item
    def close_spider(self, spider):
        self.cursor.close()
        self.conn.close()

2.9 Scrapy异步保存数据到mysql

import pymysql
from itemadapter import ItemAdapter
from twisted.enterprise import adbapi
class JianshuPipeline:
	# 同步保存数据
    def __init__(self):
        db_params = {
            'host': '172.17.2.36',
            'port': 3306,
            'user': 'root',
            'password': 'xxxxx',
            'database': 'spider',
            'charset': 'utf8'
        }
        self.conn = pymysql.connect(**db_params)
        self.cursor = self.conn.cursor()
        self._sql = None
    def process_item(self, item, spider):
        self.cursor.execute(self.sql, (item['title'], item['content'], item['author'], item['avatar'], item['pubdate'], item['origin_url'], item['article_id']))
        self.conn.commit()
        return item
    @property
    def sql(self):
        if not self._sql:
            self._sql = 'insert into article(id, title, content, author, avatar, pubdate, origin_url, article_id) values(null, %s, %s ,%s, %s, %s, %s, %s)'
        return self._sql

class JianShuTwistedPipeline:
	# 异步保存数据
    def __init__(self):
        db_params = {
            'host': '172.17.2.36',
            'port': 3306,
            'user': 'root',
            'password': 'xxxxx',
            'database': 'spider',
            'charset': 'utf8',
            'cursorclass': pymysql.cursors.DictCursor
        }
        self.db_pool = adbapi.ConnectionPool('pymysql', **db_params)
        self._sql = None

    @property
    def sql(self):
        if not self._sql:
            self._sql = 'insert into article(id, title, content, author, avatar, pubdate, origin_url, article_id) values(null, %s, %s ,%s, %s, %s, %s, %s)'
        return self._sql

    def process_item(self, item, spider):
        defer = self.db_pool.runInteraction(self.insert_item, item)
        defer.addErrback(self.handle_error, item)
        return item

    def insert_item(self, cursor, item):
        cursor.execute(self.sql, (item['title'], item['content'], item['author'], item['avatar'], item['pubdate'], item['origin_url'], item['article_id']))

    def handle_error(self, error, spider):
        print('=' * 10 + 'error' + '=' * 10)
        print(error)

三:发送post请求

scrapy默认每次发送请求是get请求,如果要发送post请求,需要重写start_requests方法

import scrapy
class RrSpider(scrapy.Spider):
    name = 'rr'
    allowed_domains = ['renren.com']
    start_urls = ['http://renren.com/']
    # scrapy默认发送的是get请求,因为要登录发送post请求,所以重写start_requests方法
    def start_requests(self):
        url = 'http://www.renren.com/PLogin.do'
        data = {'email': 'ginvip@qq.com', 'password': 'xxxxxxxx'}
        # FormRequest方法可以post表单数据
        request = scrapy.FormRequest(url, formdata = data, callback=self.parse_page)
        yield request
    def parse_page(self, response):
        request = scrapy.Request(url = 'http://www.renren.com/875198389/profile', callback = self.parse_profile)
        yield request
    def parse_profile(self, response):
        with open('profile.html', 'w', encoding='utf-8') as fp:
            fp.write(response.text)

四:附录

4.1 urllib.parse模块使用

url.parse :定义了url的标准接口,实现url的各种抽取
parse模块的使用:url的解析,合并,编码,解码
1:urlparse()实现URL的识别和分段

url = 'https://book.qidian.com/info/1004608738?wd=123&page=20#Catalog'
"""
url:待解析的url
scheme='':假如解析的url没有协议,可以设置默认的协议,如果url有协议,设置此参数无效
allow_fragments=True:是否忽略锚点,默认为True表示不忽略,为False表示忽略
"""
result = parse.urlparse(url=url,scheme='http',allow_fragments=True)
print(result)
print(result.scheme)
# (scheme='https', netloc='book.qidian.com', path='/info/1004608738', params='', query='wd=123&page=20', fragment='Catalog')
"""
scheme:表示协议
netloc:域名
path:路径
params:参数
query:查询条件,一般都是get请求的url
fragment:锚点,用于直接定位页
面的下拉位置,跳转到网页的指定位置
"""

2:urlunparse()可以实现URL的构造

url_parmas = ('https', 'book.qidian.com', '/info/1004608738', '', 'wd=123&page=20', 'Catalog')
# components:是一个可迭代对象,长度必须为6
result = parse.urlunparse(url_parmas)
print(result)
"""
https://book.qidian.com/info/1004608738?wd=123&page=20#Catalog
"""

3:urljoin()传递一个基础链接,根据基础链接可以将某一个不完整的链接拼接为一个完整链接

base_url = 'https://book.qidian.com/info/1004608738?wd=123&page=20#Catalog'
sub_url = '/info/100861102'
# 注意:sub_url的值前面必须要有一个/,如果没有/会将sub_url直接拼接到base_url的后面
full_url = parse.urljoin(base_url,sub_url)
print(full_url)		# https://book.qidian.com/info/100861102
# 理解:将base_url中的scheme='https', netloc='book.qidian.com',后面的不要,然后与sub_url进行拼接

4:urlencode()将字典构形式的参数序列化为url编码后的字符串(常用来构造get请求和post请求的参数)k1=v1&k2=v2

parmas = {
    'wd':'123',
    'page':20
}
parmas_str = parse.urlencode(parmas)
print(parmas_str)
"""
page=20&wd=123
"""
parse_qs()将url编码格式的参数反序列化为字典类型
parmas_str = 'page=20&wd=123'
parmas = parse.parse_qs(parmas_str)
print(parmas)
"""
{'page': ['20'], 'wd': ['123']}
"""

5:quote()可以将中文转换为URL编码格式

word = '中国梦'
url = 'http://www.baidu.com/s?wd='+parse.quote(word)
print(parse.quote(word))
print(url)
"""
%E4%B8%AD%E5%9B%BD%E6%A2%A6
http://www.baidu.com/s?wd=%E4%B8%AD%E5%9B%BD%E6%A2%A6
"""
unquote:可以将URL编码进行解码
url = 'http://www.baidu.com/s?wd=%E4%B8%AD%E5%9B%BD%E6%A2%A6'
print(parse.unquote(url))
"""
http://www.baidu.com/s?wd=中国梦
"""

4.2 xpath的css选择器

Python爬虫之lxml模块
Python爬虫之lxml模块
Python爬虫之lxml模块

class CnblogsSpider(scrapy.Spider):
    name = 'cnblogs'
    allowed_domains = ['news.cnblogs.com']
    start_urls = ['http://news.cnblogs.com/']
    def parse(self, response):
        post_nodes = response.css('#news_list .news_block')[:1]
        for post_node in post_nodes:
            image_url = post_node.css('.entry_summary a img::attr(href)').extract_first()
            post_url = post_node.css('h2 a::attr(href)').extract_first()
            yield scrapy.Request(url = parse.urljoin(response.url, post_url), meta = {'front_image_url': image_url}, callback = self.parse_detail)
        # next url
        next_url = response.css('div.pager a:last-child::text').extract_first()
        if 'Next' in next_url:
            next_url = response.css('div.pager a:last-child::attr(href)').extract_first()
            yield scrapy.Request(url=parse.urljoin(response.url, next_url), callback=self.parse)
    def parse_detail(self, response):
        # https://news.cnblogs.com/NewsAjax/GetAjaxNewsInfo?contentId=661951
        content_id = re.match('.*?(\d+)', response.url)
        content_id = content_id.group(1)
        if content_id:
            title = response.css('#news_title a::text').extract_first()
            create_date = response.css('#news_info .time::text').extract_first()
            content = response.css('#news_content').extract_first()
            tag_list = response.css('.news_tags a::text').extract()
            tags = ','.join(tag_list)
            json_url = parse.urljoin(response.url, '/NewsAjax/GetAjaxNewsInfo?contentId={}'.format(content_id))
            html = requests.get(json_url)
            data = html.json()
            praise_num = data.get('DiggCount')
            fav_nums = data.get('TotalView')
            comment_nums = data.get('CommentCount')
            pass

4.3 Scrapy的Request与Response

# Request源码
class Request(object_ref):

    def __init__(self, url, callback=None, method='GET', headers=None, body=None,
                 cookies=None, meta=None, encoding='utf-8', priority=0,
                 dont_filter=False, errback=None, flags=None, cb_kwargs=None):

        self._encoding = encoding  # this one has to be set first
        self.method = str(method).upper()
        self._set_url(url)
        self._set_body(body)
        if not isinstance(priority, int):
            raise TypeError("Request priority not an integer: %r" % priority)
        self.priority = priority

        if callback is not None and not callable(callback):
            raise TypeError('callback must be a callable, got %s' % type(callback).__name__)
        if errback is not None and not callable(errback):
            raise TypeError('errback must be a callable, got %s' % type(errback).__name__)
        self.callback = callback
        self.errback = errback

        self.cookies = cookies or {}
        self.headers = Headers(headers or {}, encoding=encoding)
        self.dont_filter = dont_filter

        self._meta = dict(meta) if meta else None
        self._cb_kwargs = dict(cb_kwargs) if cb_kwargs else None
        self.flags = [] if flags is None else list(flags)
.......................        

Request对象
Request 对象表示一个HTTP请求,它通常是在爬虫生成,并由下载执行,从而生成Response 有请求才有响应
Request 对象在我们写爬虫,爬取一页的数据需要重新发送一个请求的时候调用。这个类需要传递一些参数,其中比较常用的参数有:

url (string) 这个request对象发送请求的url。
callback(callback) 在下载器下载完相应的数据后执行的回调函数。
method (string) 请求的方法。默认为GET方法,常用还有 POST ,注意是大写。
headers(dict) 请求头,对于一些固定的设置,放在 settings.py 中指定就可以了。对于那些非固定的,可以在发送请求的时候指定。
body(strunicode) 请求体。如果unicode传递了a,那么它被编码为 str使用传递的编码(默认为utf-8)。如果 body没有给出,则存储一个空字符串。不管这个参数的类型,存储的最终值将是一个str(不会是unicodeNone)。
cookie(dictlist) 请求cookie。这些可以以两种形式发送。
meta(dict) 比较常用。用于在不同的请求之间传递数据用的。
encoding 编码。默认的为utf-8,使用默认的就可以了。
priority(int) 此请求的优先级(默认为0)。调度器使用优先级来定义用于处理请求的顺序。具有较高优先级值的请求将较早执行。允许负值以指示相对低优先级。
dont_filter(boolean) 表示不由调度器过滤。在执行多次重复的请求的时候用得比较多, 默认为False。
errback(callback) 在发生错误的时候执行的函数。

Response对象
Response 对象一般是由Scrapy给你自动构建的。因此开发者不需要关心如何创建Response对象,而是如何使用他。Response 对象有很多属性,可以用来提取数据的。主要有以下属性:

meta 从其他请求传过来的meta 属性,可以用来保持多个清求之向的数据连接。
encoding 返回当前字符串编码和解码的格式。
text 将返回来的数据为 unicode 字符串返回。
body 将返回来的数据作为 bytes 字符串返回。
xpath xapth迭代器。
css css迭代器。

发送POST靖求
有吋候问想要在请求数据的吋候发送POST请求,那么这吋候需要使用Request的子类FormRequest 来实现。如果想要在爬虫一开始的时候就发送POST请求,那么需要在爬虫类中重写 start. requests(self) 方法,并且不再调用 start_urls里的 url。

您可能感兴趣的与本文相关的镜像

Python3.9

Python3.9

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值