回到自己电脑:本地环境与练习网站
3 道练习题·预计 50 分钟·做对一题解锁下一段
前言
各位童鞋,前 9 章的代码都是在网页里运行的,抓的也都是本站的两点书店。在网页里运行很方便,什么都不用装。可它也有限制:只能抓 learn-py.org 上的网页,不能自己设 User-Agent,而且每次运行最多 5 秒。
所以从这一章开始,咱们回到自己电脑上。在自己电脑上,这些限制就都没有了:requests 能去请求任何网站,能带上自己的请求头,想跑多久就跑多久。不过,本事大了,第 1 章立下的那些规矩,也就更要紧了。
那抓什么网站来练手呢?咱们换成一个专门给初学者练手的公开网站:toscrape。它的首页写得明明白白,自己就是一个给人练习抓取的沙箱。抓它,既能遇到真实网站上的各种情况,又不用担心给谁添麻烦。
还要提前说一句:第一次在自己电脑上装包、配环境,难免会遇到各种报错,比如命令找不到、包装到了别的地方、网络连不上。碰到了别着急,按实战 3 的办法,先看报错的最后一行,再一步一步地排查。环境只要配好一次,以后就都顺了。
学这一章之前,需要先学完 爬虫课第 9 章 ,并且已经按 基础课第 1 章 的说明,在自己电脑上装好了 Python 3.10 或更新的版本。
目录
这一章会学到:
- 一、准备环境——虚拟环境和
pip install - 二、在自己电脑上跑书店爬虫——同样的代码,原样能跑
- 三、抓一个真实的练习网站——books.toscrape.com
- 四、说明自己是谁——User-Agent
- 五、常见坑
- 六、自己查一次文档——requests 是怎么决定编码的
- 七、小结与回看
学完这一章,你能在自己电脑上配好爬虫的环境,礼貌地抓取一个真实的网站,并处理好编码这类网页里遇不到的问题。
一、准备环境
在自己电脑上写爬虫,第一件事是把环境准备好。先看看电脑上的 Python 版本够不够新。打开终端(Windows 上是「命令提示符」或 PowerShell),输入:
python3 --versionWindows 上一般用 python --version 。只要看到 Python 3.10 或者更高的版本号,就可以了。
版本没问题,接下来给爬虫建一个单独的文件夹,在里面建一个虚拟环境。虚拟环境是什么呢?简单说,就是专门给这个项目准备的一套 Python 包,和电脑上别的项目互不干扰。用下面这三条命令建好它:
mkdir my-crawler
cd my-crawler
python3 -m venv .venv前两条命令建好文件夹,再进到里面。最后一条 python3 -m venv .venv ,意思是在当前文件夹里,建一个名叫 .venv 的虚拟环境。建好了还不能直接用,接着还得激活它:
source .venv/bin/activateWindows 上要换成 .venv\Scripts\activate 。激活以后,命令行的前面会多出 (.venv) 这几个字,说明现在用的,就是这个虚拟环境里的 Python 了。
虚拟环境准备好了,最后装上这门课用到的两个包:
python -m pip install requests beautifulsoup4装好以后,试一试能不能导入:
python -c "import requests, bs4; print(requests.__version__, bs4.__version__)"能打印出两个版本号,环境就准备好了。
这几步里,有几个地方要说明一下:
- 包的名字叫
beautifulsoup4,可导入的时候要写bs4,第 4 章说过。 - 装包用的是
python -m pip,而不是直接敲pip。这样能保证,包装进的就是当前这个 Python。为什么要这样,第五节「常见坑」会讲。 - 每次重新打开终端,都要先
cd到这个文件夹,再激活一次虚拟环境。 - 基础课第 23 章会讲一个更新的工具
uv,用它管理环境和包更省事。现在先用 Python 自带的venv和pip。
环境配好了,先拿上一章写好的书店爬虫来试一试。
二、在自己电脑上跑书店爬虫
在网页里写的爬虫,搬到自己电脑上,还能不能跑?试一试就知道了。
把第 9 章「六、完整的爬虫」里的那段代码,原样存成 bookstore.py ,放在 my-crawler 文件夹里。然后在激活了虚拟环境的终端里,运行它:
python bookstore.py在自己电脑上运行,输出是这样的:
接口:取到 120 本
校验:第 1 页 20 本全部对得上
已存成 my_bookstore.csv和在网页里运行的结果一模一样。再看看文件夹,里面多了一个 my_bookstore.csv ,用记事本或者 Excel 都能打开。这回,它是真的存在你的硬盘上了,不会刷新一下页面就没了。
可见,在网页里写的爬虫,几乎不用改,就能在自己电脑上跑。不过,有两个地方,到了自己电脑上应该改一改:
- 停顿时间:
fetch()里的pause=0.2设得这么短,是因为网页里每次运行最多只有 5 秒。在自己电脑上没有这个限制,就改成pause=1或者更长。 - 说明自己是谁:网页里改不了 User-Agent,在自己电脑上就可以带上了。怎么带,第四节就讲。
顺便说一句:books.csv 也可以不用自己抓,从本站的 练习靶场 直接下载。以后数据分析课在自己电脑上运行时,需要的数据文件也都在那里。
自己写的爬虫,在自己电脑上能跑了。下一节,咱们就去抓一个真正的、别人的网站。
三、抓一个真实的练习网站
抓哪个网站呢?就是前言里提到的 toscrape。toscrape.com 是一组专门给人练习抓取的网站。它的首页说得很清楚:其中的 books.toscrape.com 是一家虚构的书店,「迫切地希望被抓取」,是初学者练习的安全场所。这一节就来抓它。
不过要先说在前面:它毕竟是别人的网站,以后可能会改版,也可能关闭。如果下面的代码跑不通了,先在浏览器里打开看看,网站还在不在、结构变没变。实在不行,就回到两点书店练习。
抓别人的网站,第一件事做什么?当然是先看看人家的规矩。
第一步,看 robots.txt。 到了自己电脑上,RobotFileParser 可以用 read() 自己去把 robots.txt 取回来了:
from urllib.robotparser import RobotFileParser
parser = RobotFileParser('https://books.toscrape.com/robots.txt')
parser.read()
print(parser.can_fetch('*', 'https://books.toscrape.com/catalogue/page-2.html'))在自己电脑上运行,输出是:
True结果是 True ,可以抓。不过这里有个小细节:这个网站其实没有 robots.txt,请求它会得到 404。没有规则,RobotFileParser 就认为全部都可以抓,True 就是这么来的。
规矩看过了,接下来就可以取网页了。
第二步,取首页看看。 除了第一本书的价格,顺便把状态码、响应头里的 content-type 和 requests 用的编码,也都打印出来:
import requests
from bs4 import BeautifulSoup
response = requests.get('https://books.toscrape.com/')
print(response.status_code)
print(response.headers['content-type'])
print(response.encoding)
soup = BeautifulSoup(response.text, 'html.parser')
print(soup.select_one('article.product_pod .price_color').get_text())在自己电脑上运行,输出是:
200
text/html
ISO-8859-1
£51.77状态码是 200,网页取到了。可是看最后一行,价格前面多了个  !这是怎么回事?
这就是第 2 章「常见坑」里说过的乱码。网站的 content-type 只写了 text/html ,没写 charset ,requests 就按 ISO-8859-1 来解码。可网页其实是 UTF-8 的,这么一解码,英镑符号 £ 就成了 £ 。
那为什么在两点书店里,从来没遇到过这个问题呢?因为两点书店的响应头里写清了编码。到了真实的网站,就碰上了。
原因找到了,修起来就简单了。
第三步,告诉 requests 正确的编码,再取数据。 先在开发者工具里看看网页的结构:每本书是一个 article.product_pod ,书名完整地写在链接的 title 属性里,价格在 .price_color 里。照着这个结构来写:
import requests
from bs4 import BeautifulSoup
response = requests.get('https://books.toscrape.com/')
response.encoding = 'utf-8'
soup = BeautifulSoup(response.text, 'html.parser')
for book in soup.select('article.product_pod')[:3]:
title = book.h3.a['title']
price = float(book.select_one('.price_color').get_text().replace('£', ''))
print(title, price)在自己电脑上运行,输出是:
A Light in the Attic 51.77
Tipping the Velvet 53.74
Soumission 50.1可以看到,response.encoding = 'utf-8' 这一行一加,£ 就正常了。再把 £ 去掉,价格就能转成数字。
还有一个问题:书名为什么要取 title 属性,而不是直接取链接的文字呢?在开发者工具里看一眼就知道了:书名长的时候,链接上显示的文字是截断的,后面带着 ... ,完整的书名只有 title 属性里才有。所以写选择器之前,一定要先去开发者工具里,看看网页实际的样子。
修乱码、取完整的书名,这两件事下面各有一道练习,先动手做一做。
raw 是网站发回来的原始字节(相当于 response.content ),网页本身是 UTF-8 编码的。请:
- 用
'iso-8859-1'解码,交给 BeautifulSoup,打印p标签的文字,看看乱码长什么样; - 用
'utf-8'正确解码,再打印一次; - 去掉
£,把价格转成浮点数打印。
运行后的输出应该是:
£51.77
£51.77
51.77推荐一个,全网最全最友好的中文 AI 编程指南: coding.stormzhang.ai