Python爬虫初阶

type
Post
status
Published
summary
之前零散的学过一些爬虫,并且最近自己用的也比较频繁,借此机会整理一篇大体的爬虫框架笔记。一个基本的爬虫通常分为网页下载、网页解析和数据存储三个部分的内容。
slug
python-spider-1
date
Feb 9, 2023
tags
爬虫
蜘蛛
Selenium
Scrapy
category
基础知识
password
icon
URL
Property
Feb 28, 2024 01:07 PM
 
一个基本的爬虫通常分为数据采集(网页下载)、数据处理(网页解析)和数据存储(将有用的信息持久化)三个部分的内容。

一、数据采集

数据采集主要是向目标网址发起请求并获取到目标网址的数据(这里的数据通常是指网页的源代码,第二步就需要对网页的源代码进行解析以便获取到我们真正想要的数据)。
在使用Python爬虫时,需要模拟发起网络请求,主要用到的库有requests库和python内置的urllib库,现在一般建议使用requests,它是对urllib的再次封装,功能强大并且使用起来也更优雅。

1.1、requests简介

requests是基于 Python 标准库进行了封装,简化了通过 HTTP 或 HTTPS 访问网络资源的操作。通过requests库,我们可以让 Python 程序向浏览器一样向 Web 服务器发起请求,并接收服务器返回的响应,从响应中我们就可以提取出想要的数据。
实例:
import requests resp = requests.get('https://www.sohu.com/') if resp.status_code == 200: print(resp.text)

1.2、requests.get参数详解

requests.get(url=url,headers=headers,timeout=10,params=params,auth=auth,verify=False,proxies=proxies) # url: 需要抓取的URL地址 # headers: 请求头,从浏览器中获取 # timeout: 超时时间;数值或元组类型;超过时间会抛出异常;如果只给一个数字,则会自动处理为这个数字组成的二维元组;如给10为参数值, # 会被处理为(10,10),此时两个数字分别表示连接超时和读取超时,所以也可以直接给元组作为参数,可以设定两个不同的值;如(10,15) # params: 查询参数;字典类型;将字典中键值对作为查询参数拼接到url参数的地址上,此时的url为基准的url地址,不包含查询参数 # auth: Web客户端验证参数;元组类型('username','password');针对于需要web客户端用户名密码认证的网站 # verify: SSL证书认证参数;布尔类型;True(默认)检查证书认证;False(常用)忽略证书认证; # SSL证书认证参适用于没有经过证书认证机构认证的https类型网站,一般这种网站会抛出SSLError异常则考虑使用此参数 # proxies: 网络代理参数;字典类型;代替你原来的IP地址去对接网络的IP地址。隐藏自身真实IP,避免被封。 # allow_redirects: 是否重定向;布尔类型;True(默认)是;False(常用)否; # stream: 是否重定向;布尔类型;True(常用)是;False(默认)否; # 默认情况下是stream=Ffalse,他会立即开始下载文件并存放到内存当中,倘若文件过大就会导致内存不足的情况. # 详情参考:[requests 中的参数 stream - 天马行宇 - 博客园](https://www.cnblogs.com/yuanyongqiang/p/12609860.html)
requests.get() 返回对象属性
encoding : 响应字符编码 text : 字符串 网站源码 content : 字节流 字符串网站源码 status_code : HTTP响应码 url : 实际数据的URL地址
res = requests.get('http://www.baidu.com/') print(res.encoding) # 查看网站的编码格式 print(res.status_code) # 查看响应码 200 print(res.url) # 查看访问的URL地址 https://www.baidu.com/ # text属性获取响应内容(字符串)网站源码 res.encoding = 'utf-8' html = res.text # content属性获取响应内容(字节串 bytes)网站源码;通常用于非结构化数据的保存,如压缩文件zip、图片文件等;下文会详细介绍保存方法。 html = res.content.decode('utf-8')

二、数据解析

以下不同方法的解析案例都以【如何从豆瓣电影获取排前250名的电影的名称】为例;
豆瓣电影TOP250每页共展示了25部电影,如果要获取全部数据,我们共需要访问10个页面,对应的地址是https://movie.douban.com/top250?start=xxx,这里的xxx如果为0就是第一页,如果xxx的值是100,那么我们可以访问到第五页。

2.1、正则解析

import random import re import time import requests for page in range(1, 11): resp = requests.get( url=f'https://movie.douban.com/top250?start={(page - 1) * 25}', # 如果不设置HTTP请求头中的User-Agent,豆瓣会检测出不是浏览器而阻止我们的请求。 # 通过get函数的headers参数设置User-Agent的值,具体的值可以在浏览器的开发者工具查看到。 # 用爬虫访问大部分网站时,将爬虫伪装成来自浏览器的请求都是非常重要的一步。 headers={'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36'} ) # 通过正则表达式获取class属性为title且标签体不以&开头的span标签并用捕获组提取标签内容 pattern1 = re.compile(r'<span class="title">([^&]*?)</span>') titles = pattern1.findall(resp.text) # 通过正则表达式获取class属性为rating_num的span标签并用捕获组提取标签内容 pattern2 = re.compile(r'<span class="rating_num".*?>(.*?)</span>') ranks = pattern2.findall(resp.text) # 使用zip压缩两个列表,循环遍历所有的电影标题和评分 for title, rank in zip(titles, ranks): print(title, rank) # 随机休眠1-5秒,避免爬取页面过于频繁 time.sleep(random.random() * 4 + 1)

2.2、XPath 解析

XPath 是在 XML(eXtensible Markup Language)文档中查找信息的一种语法,XPath 使用路径表达式来选取 XML 文档中的节点或者节点集,这里所说的节点包括元素、属性、文本、命名空间、处理指令、注释、根节点等。
实现 XPath 解析需要三方库lxml的支持,可以使用下面的命令安装lxml
pip install lxml
用 XPath 解析方式改写之前获取豆瓣电影 Top250的代码:
from lxml import etree import requests for page in range(1, 11): resp = requests.get( url=f'https://movie.douban.com/top250?start={(page - 1) * 25}', headers={'User-Agent': 'BaiduSpider'} ) tree = etree.HTML(resp.text) # 通过XPath语法从页面中提取电影标题 title_spans = tree.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[1]/a/span[1]') # 通过XPath语法从页面中提取电影评分 rank_spans = tree.xpath('//*[@id="content"]/div/div[1]/ol/li[1]/div/div[2]/div[2]/div/span[2]') for title_span, rank_span in zip(title_spans, rank_spans): print(title_span.text, rank_span.text)
 
XPath解析原理:
1、实例话entree对象,将需要解析的页面源码数据加载到对象中
  • 将本地的html文件中源码数据加载到etree中
    • tree = etree.parse(filePath)
  • 将互联网中获取的带源码数据加载到etrre中
    • tree = etree.HTML('page_text')
2、调用etree对象中的XPath方法结合XPath表达式实现内容定位和捕获
tree.xpath('xpath表达式') # 返回的是列表
XPath表达式详解:
/:表示从根节点开始定位,表示一个层级;实例:tree.xpath('/html/body/div') //://在中间,表示多个层级(两个或两个以上);实例:tree.xpath('/html//div') //://在开头,表示从任意位置开始定位;实例tree.xpath('//div') [@]:属性定位;实例:tree.xpath("//div[@class='song']");tree.xpath("//tag[@attrName='attrValue']") [n]:索引定位:实例:tree.xpath("//div[@class='song']/p[3]") 索引是从1开始的 /text():获取直系文本;实例:tree.xpath('//div[class="tang"]//li[5]/a/text()')[0] //text():获取非直系文本(标签下所有文本);实例:tree.xpath('//li[7]//text()') /@attrName:获取属性;实例:tree.xpath('//div[class="song"]/img/@src')
 
常见的XPath语法如下:
notion imagenotion image
notion imagenotion image
notion imagenotion image
XPath具体教程详见:菜鸟教程——XPath教程

2.3、CSS 选择器解析

浏览器中运行的 JavaScript 本身就可以document对象的querySelector()和querySelectorAll()方法基于 CSS 选择器获取页面元素。在 Python 中,我们可以利用三方库beautifulsoup4或pyquery来做同样的事情。Beautiful Soup 可以用来解析 HTML 和 XML 文档,修复含有未闭合标签等错误的文档,通过为待解析的页面在内存中创建一棵树结构,实现对从页面中提取数据操作的封装。
安装Beautiful Soup:
pip install beautifulsoup4
使用bs4改写的获取豆瓣电影Top250电影名称的代码:
import bs4 import requests for page in range(1, 11): resp = requests.get( url=f'https://movie.douban.com/top250?start={(page - 1) * 25}', headers={'User-Agent': 'BaiduSpider'} ) # 创建BeautifulSoup对象 soup = bs4.BeautifulSoup(resp.text, 'lxml') # 通过CSS选择器从页面中提取包含电影标题的span标签 title_spans = soup.select('div.info > div.hd > a > span:nth-child(1)') # 通过CSS选择器从页面中提取包含电影评分的span标签 rank_spans = soup.select('div.info > div.bd > div > span.rating_num') for title_span, rank_span in zip(title_spans, rank_spans): print(title_span.text, rank_span.text)
bs4具体教程详见:官方文档

2.4、简单的总结

notion imagenotion image

三、数据存储

数据存储根据实际的需求有很多方式,文本数据直接存储到本地(txt)或结合Excel、数据库等工具的使用;二进制文件如压缩文件、图片等需要读写到本地。
二进制资源以百度首页的logo图片为例
import requests resp = requests.get('https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png') with open('baidu.png', 'wb') as file: file.write(resp.content)

四、重要的其他内容

4.1、用Selenium抓取网页动态内容

4.1.1、Selenium简介

根据权威机构发布的全球互联网可访问性审计报告,全球约有四分之三的网站其内容或部分内容是通过JavaScript动态生成的,这就意味着在浏览器窗口中“查看网页源代码”时无法在HTML代码中找到这些内容,也就是说我们之前用的抓取数据的方式无法正常运转了。解决这样的问题基本上有两种方案,一是获取提供动态内容的数据接口,这种方式也适用于抓取手机 App 的数据;另一种是通过自动化测试工具 Selenium 运行浏览器获取渲染后的动态内容。对于第一种方案,我们可以使用浏览器的“开发者工具”或者更为专业的抓包工具(如:Charles、Fiddler、Wireshark等)来获取到数据接口,后续的操作同上;第二种方案主要使用自动化测试工具 Selenium 来获取网站的动态内容。
Selenium 是一个自动化测试工具,利用它可以驱动浏览器执行特定的行为,最终帮助爬虫开发者获取到网页的动态内容。简单的说,只要我们在浏览器窗口中能够看到的内容,都可以使用 Selenium 获取到,对于那些使用了 JavaScript 动态渲染技术的网站,Selenium 会是一个重要的选择。下面,我们还是以 Chrome 浏览器为例,来讲解 Selenium 的用法,大家需要先安装 Chrome 浏览器并下载它的驱动。Chrome 浏览器的驱动程序可以在ChromeDriver官网进行下载,驱动的版本要跟浏览器的版本对应,如果没有完全对应的版本,就选择版本代号最为接近的版本。

4.1.2、Selenium使用

安装 Selenium
pip install selenium
下载浏览器驱动:http://chromedriver.storage.googleapis.com/index.html (根据自己的浏览器版本下载对应的驱动)
加载页面:通过下面的代码驱动 Chrome 浏览器打开百度
from selenium import webdriver # 创建Chrome浏览器对象 browser webdriver.Chrome(executable_path='/***/chromedriver',) # executable_path驱动文件的位置 # 加载指定的页面 browser.get('https://www.baidu.com/')
查找元素和模拟用户行为
接下来,我们可以尝试模拟用户在百度首页的文本框输入搜索关键字并点击“百度一下”按钮。在完成页面加载后,可以通过Chrome对象的find_element和find_elements方法来获取页面元素,Selenium 支持多种获取元素的方式,包括:CSS 选择器、XPath、元素名字(标签名)、元素 ID、类名等,前者可以获取单个页面元素(WebElement对象),后者可以获取多个页面元素构成的列表。获取到WebElement对象以后,可以通过send_keys来模拟用户输入行为,可以通过click来模拟用户点击操作,代码如下所示。
from selenium import webdriver from selenium.webdriver.common.by import By browser = webdriver.Chrome() browser.get('https://www.baidu.com/') # 通过元素ID获取元素 kw_input = browser.find_element(By.ID, 'kw') # 模拟用户输入行为 kw_input.send_keys('Python') # 通过CSS选择器获取元素 su_button = browser.find_element(By.CSS_SELECTOR, '#su') # 模拟用户点击行为 su_button.click()
无头模式(无可视化界面,浏览器后台运行)
⚠️
如果使用操作链保存图片,则无法使用无头模式(操作链的输入和点击是需要在浏览器界面完成的,不限时浏览器界面,相应的键盘输入会输入到当前屏幕聚焦的应用中)
from selenium.webdriver.chrome.options import Options chrome_options = Options() chrome_options.add_argument('--headless') # 添加无头参数 bro = webdriver.Chrome(options=chrome_options)
修改浏览器默认参数和防范被识别风险
⚠️
设置的路径需要输入绝对路径
# 设置浏览器路径 filepath_options = webdriver.ChromeOptions() # 浏览器首选项 prefs = { 'profile.default_content_settings.popups': 0, # 设置为 0 禁止弹出窗口 'excludeSwitches': ['enable-automation'], # 用来规避门户网站识别selenium请求 'savefile.default_directory': f'/***/{img_folder}', # 设置保存路径 'download.default_directory': f'/***/{img_folder}' # 设置下载路径 } filepath_options.add_experimental_option('prefs', prefs) # 初始化浏览器对象,传入浏览器驱动 # service = bro = webdriver.Chrome(executable_path='/***/chromedriver', chrome_options=filepath_options)

4.1.3、常用的属性和方法

以下是浏览器对象和WebElement对象常用的属性和方法。具体的内容大家还可以参考 Selenium 官方文档的中文翻译
notion imagenotion image
notion imagenotion image
notion imagenotion image
notion imagenotion image
 

4.1.4、360图片下载实例

下面的例子演示了如何使用 Selenium 从“360图片”网站搜索和下载图片。
import os import time from concurrent.futures import ThreadPoolExecutor import requests from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys DOWNLOAD_PATH = 'images/' def download_picture(picture_url: str): """ 下载保存图片 :param picture_url: 图片的URL """ filename = picture_url[picture_url.rfind('/') + 1:] resp = requests.get(picture_url) with open(os.path.join(DOWNLOAD_PATH, filename), 'wb') as file: file.write(resp.content) if not os.path.exists(DOWNLOAD_PATH): os.makedirs(DOWNLOAD_PATH) browser = webdriver.Chrome() browser.get('https://image.so.com/z?ch=beauty') browser.implicitly_wait(10) kw_input = browser.find_element(By.CSS_SELECTOR, 'input[name=q]') kw_input.send_keys('苍老师') kw_input.send_keys(Keys.ENTER) for _ in range(10): browser.execute_script( 'document.documentElement.scrollTop = document.documentElement.scrollHeight' ) time.sleep(1) imgs = browser.find_elements(By.CSS_SELECTOR, 'div.waterfall img') with ThreadPoolExecutor(max_workers=32) as pool: for img in imgs: pic_url = img.get_attribute('src') pool.submit(download_picture, pic_url)

4.1.5、实操案例:使用操作链保存图片

⚠️
由于本案例的门户有反爬措施,使用requests向图片发起请求无法获取到数据,所以在浏览器里面使用自动化操作链来下载内容。 本案例使用截止目前为止最新版本的谷歌浏览器110.0.5481.77;网上常见的使用【右键图片——输入v下载图片】模式以失效,本案例对其进行了小改动,并成功下载图片。
import warnings import time from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.common.action_chains import ActionChains import pyautogui # 操作鼠标键盘 surs = [] for n, url in enumerate(surs): """ 先使用requests模块拿到链接和标题,根据标题创建文件夹,然后再把创建后的文件夹路径设置为浏览器的默认路径,大功告成! """ img_folder = pag_num + '——' + pag_title """ 拿到下载路径之后,开始请求下载 """ # warnings.simplefilter('ignore',ResourceWarning) # 去除开头警告 # 设置浏览器路径 filepath_options = webdriver.ChromeOptions() # 浏览器首选项 prefs = { 'profile.default_content_settings.popups': 0, # 设置为 0 禁止弹出窗口 'excludeSwitches': ['enable-automation'], # 用来规避门户网站识别selenium请求 'savefile.default_directory': f'/Users/ayd/Downloads/其他/20230204/spider/photos18/{img_folder}', # 设置保存路径 'download.default_directory': f'/Users/ayd/Downloads/其他/20230204/spider/photos18/{img_folder}' # 设置下载路径 } filepath_options.add_experimental_option('prefs', prefs) # 初始化浏览器对象,传入浏览器驱动 # service = bro = webdriver.Chrome(executable_path='/Users/ayd/package/Chrome插件/chromedriver_mac_arm64(selenium驱动)/chromedriver', chrome_options=filepath_options) # # 让浏览器发起请求 bro.get(url=url) pag_text = bro.page_source # 获取网站源码数据 # print('这里是pag_text:',pag_text) # 下载图片 for i,img_url in enumerate(img_list): # 通过执行js代码,将图片url在新的标签页中打开 js = f"window.open(\'{img_url}\')" bro.execute_script(js) # 切换窗口:bro.window_handles获取所有窗口组成的列表;bro.switch_to.window切换窗口 bro.switch_to.window(bro.window_handles[-1]) time.sleep(1) # 等待图片加载1s # 保存图片系列操作 pic = bro.find_element(By.XPATH, '/html/body/img') # 获取元素 action = ActionChains(bro).move_to_element(pic) # 移动到图片 action.context_click(pic).perform() # 右击并执行 pyautogui.typewrite(['s','down','enter']) # 操作键盘输入s(这里输入什么都行,只是为了选中右键出来的菜单),然后输入方向键选择“图片另存为”,最后输入回车确认 time.sleep(1) # 等待保存窗口弹出 pyautogui.typewrite(['enter']) # 回车确认 # time.sleep(1) bro.close() # 关闭标签页 bro.switch_to.window(bro.window_handles[-1]) # 切换回初始标签页,不然会报错 # time.sleep(1) print(f'{img_folder}——{n+1}/{len(surs)}完成进度:{i+1}/{len(img_list)}') bro.quit() # 完成一个页面之后退出浏览器 print(f'{img_folder}的{len_original}个文件已完成,整体完成进度:{n+1}/{len(surs)}') print('所有页已完成!')

4.2、Scrapy框架

当你写了很多个爬虫程序之后,你会发现每次写爬虫程序时,都需要将页面获取、页面解析、爬虫调度、异常处理、反爬应对这些代码从头至尾实现一遍,这里面有很多工作其实都是简单乏味的重复劳动。那么,有没有什么办法可以提升我们编写爬虫代码的效率呢?答案是肯定的,那就是利用爬虫框架,而在所有的爬虫框架中,Scrapy 应该是最流行、最强大的框架。Scrapy 是基于 Python 的一个非常流行的网络爬虫框架,可以用来抓取 Web 站点并从页面中提取结构化的数据。

4.2.1、Scrapy的组件

我们先来说说 Scrapy 中的组件。
  1. Scrapy 引擎(Engine):用来控制整个系统的数据处理流程。
  1. 调度器(Scheduler):调度器从引擎接受请求并排序列入队列,并在引擎发出请求后返还给它们。
  1. 下载器(Downloader):下载器的主要职责是抓取网页并将网页内容返还给蜘蛛(Spiders)。
  1. 蜘蛛程序(Spiders):蜘蛛是用户自定义的用来解析网页并抓取特定URL的类,每个蜘蛛都能处理一个域名或一组域名,简单的说就是用来定义特定网站的抓取和解析规则的模块。
  1. 数据管道(Item Pipeline):管道的主要责任是负责处理有蜘蛛从网页中抽取的数据条目,它的主要任务是清理、验证和存储数据。当页面被蜘蛛解析后,将被发送到数据管道,并经过几个特定的次序处理数据。每个数据管道组件都是一个 Python 类,它们获取了数据条目并执行对数据条目进行处理的方法,同时还需要确定是否需要在数据管道中继续执行下一步或是直接丢弃掉不处理。数据管道通常执行的任务有:清理 HTML 数据、验证解析到的数据(检查条目是否包含必要的字段)、检查是不是重复数据(如果重复就丢弃)、将解析到的数据存储到数据库(关系型数据库或 NoSQL 数据库)中。
  1. 中间件(Middlewares):中间件是介于引擎和其他组件之间的一个钩子框架,主要是为了提供自定义的代码来拓展 Scrapy 的功能,包括下载器中间件和蜘蛛中间件。

4.2.2、数据处理流程

Scrapy 的整个数据处理流程由引擎进行控制,通常的运转流程包括以下的步骤:
  1. 引擎询问蜘蛛需要处理哪个网站,并让蜘蛛将第一个需要处理的 URL 交给它。
  1. 引擎让调度器将需要处理的 URL 放在队列中。
  1. 引擎从调度那获取接下来进行爬取的页面。
  1. 调度将下一个爬取的 URL 返回给引擎,引擎将它通过下载中间件发送到下载器。
  1. 当网页被下载器下载完成以后,响应内容通过下载中间件被发送到引擎;如果下载失败了,引擎会通知调度器记录这个 URL,待会再重新下载。
  1. 引擎收到下载器的响应并将它通过蜘蛛中间件发送到蜘蛛进行处理。
  1. 蜘蛛处理响应并返回爬取到的数据条目,此外还要将需要跟进的新的 URL 发送给引擎。
  1. 引擎将抓取到的数据条目送入数据管道,把新的 URL 发送给调度器放入队列中。
上述操作中的第2步到第8步会一直重复直到调度器中没有需要请求的 URL,爬虫就停止工作。

4.2.3、安装和使用

pip install scrapy
接下来用scrapy实现一个爬取豆瓣电影 Top250 电影标题、评分和金句的爬虫
1、创建项目和文件
在命令行中使用scrapy命令创建名为demo的项目
scrapy startproject demo
项目的目录结构如下
demo |____ demo |________ spiders |____________ __init__.py |________ __init__.py |________ items.py |________ middlewares.py |________ pipelines.py |________ settings.py |____ scrapy.cfg
切换到demo目录,用下面的命令创建名为douban的蜘蛛程序,movie.douban.com 是指定爬取范围。
scrapy genspider douban movie.douban.com
创建好文件之后默认增加一下代码:
import scrapy class DoubanSpider(scrapy.Spider): name = 'douban' allowed_domains = ['movie.douban.com'] start_urls = ( 'http://movie.douban.com/', ) def parse(self, response): pass # name = "" : 这个爬虫的识别名称,必须是唯一的,在不同的爬虫必须定义不同的名字。 # allow_domains = []: 是搜索的域名范围,也就是爬虫的约束区域,规定爬虫只爬取这个域名下的网页,不存在的URL会被忽略。 # start_urls = (): 爬取的URL元祖/列表。爬虫从这里开始抓取数据,第一次下载的数据将会从这些urls开始。其他子URL将会从这些起始URL中继承性生成。 # parse(self, response): 解析的方法,每个初始URL完成下载后将被调用,调用的时候传入从每一个URL传回的Response对象来作为唯一参数, # 主要作用如下:负责解析返回的网页数据(response.body),提取结构化数据(生成item)生成需要下一页的URL请求。
2、明确目标数据
items.pyItem类中定义字段,这些字段用来保存数据
import scrapy class DoubanItem(scrapy.Item): title = scrapy.Field() score = scrapy.Field() motto = scrapy.Field()
3、制作爬虫
修改spiders文件夹中名为douban.py的文件,它是蜘蛛程序的核心,需要我们添加解析页面的代码
import scrapy from scrapy import Selector, Request from scrapy.http import HtmlResponse from demo.items import MovieItem class DoubanSpider(scrapy.Spider): name = 'douban' allowed_domains = ['movie.douban.com'] start_urls = ['https://movie.douban.com/top250?start=0&filter='] # 将start_urls的值修改为需要爬取的第一个url # 解析数据函数 def parse(self, response: HtmlResponse): sel = Selector(response) # 也可以使用 XPath 或正则表达式进行页面解析 movie_items = sel.css('#content > div > div.article > ol > li') for movie_sel in movie_items: item = MovieItem() item['title'] = movie_sel.css('.title::text').extract_first() item['score'] = movie_sel.css('.rating_num::text').extract_first() item['motto'] = movie_sel.css('.inq::text').extract_first() yield item hrefs = sel.css('#content > div > div.article > div.paginator > a::attr("href")') # 获取翻页按钮对应的a标签的href属性 for href in hrefs: full_url = response.urljoin(href.extract()) # 将href链接拼接到不带参数的start_urls上 yield Request(url=full_url) # 修改下次请求的Request的URL属性为拼接后的URL # 这里比较难懂,主要是要理解yield的运行规则和parse函数是自动调用的,并且url拼接是要去掉初始URLstart_urls的参数再拼接 # 推荐阅读 # [从原理到实战,一份详实的 Scrapy 爬虫教程-腾讯云](https://cloud.tencent.com/developer/article/1856557) # [如何理解Python中的yield用法? - 知乎](https://zhuanlan.zhihu.com/p/268605982)
执行爬虫
scrapy crawl douban # 这里的文件名要和name 属性对应
4、数据持久化
5、配置设置
修改settings.py文件对项目进行配置
# 用户浏览器 USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36' # 并发请求数量 CONCURRENT_REQUESTS = 4 # 下载延迟 DOWNLOAD_DELAY = 3 # 随机化下载延迟 RANDOMIZE_DOWNLOAD_DELAY = True # 是否遵守爬虫协议 ROBOTSTXT_OBEY = True # 配置数据管道 ITEM_PIPELINES = { 'demo.pipelines.MovieItemPipeline': 300, }
 
If you have any questions, please contact me.