翻页与礼貌地爬
3 道练习题·预计 50 分钟·做对一题解锁下一段
前言
上一章,咱们已经能把一页的 20 本书整整齐齐地取出来了。可是两点书店一共有 6 页,真实的网站更是动辄几十页、几百页。要是一页一页地手动去改网址,那就不叫爬虫了。
所以这一章,咱们要让爬虫自己翻页。办法有两种:一种是顺着页面上的「下一页」链接往下走,另一种是看出网址的规律,直接把每一页的地址拼出来。
能自己翻页了,请求也就跟着多了起来。这时候,第 1 章立下的规矩就要真正用起来了:每取一页,都要停一停。网站暂时出错了,就等一会儿再试,但别没完没了地试。循环要有出口,不能一直跑下去。
这些听上去都是小事,可真实的爬虫,很多就是栽在这些小事上:要么把网站拖垮了,要么被网站封掉了,要么半夜跑出一个死循环。所以写爬虫,慢一点不要紧,稳才是第一位的。
学这一章之前,需要先学完 爬虫课第 5 章 ,会用 parse_list() 把一页书取成列表,会用 urljoin() 补全链接。另外,还要熟悉基础课第 5 章的 while 循环和 break 。
目录
这一章会学到:
- 一、顺着「下一页」往下翻——
while循环加出口 - 二、看出网址规律,直接拼——先知道一共几页
- 三、出错了怎么办——超时、重试和放弃
- 四、常见坑
- 五、自己查一次文档——怎么确认真的停了 0.2 秒
- 六、小结与回看
学完这一章,你的爬虫能把整个书店的 120 本书一页不落地取下来,而且取得稳、取得有礼貌。
一、顺着「下一页」往下翻
先说第一种办法:顺着「下一页」往下翻。两点书店每一页的底部,都有一个「下一页」的链接,只有最后一页没有。这个链接在 HTML 里长什么样呢?取回第 2 页,把底部的翻页栏打印出来看看:
import requests
from bs4 import BeautifulSoup
soup = BeautifulSoup(requests.get('https://learn-py.org/lab/bookstore/page/2/').text, 'html.parser')
print(soup.select_one('nav.pager'))输出的结果:
<nav class="pager"><a class="prev" href="/lab/bookstore/page/1/">上一页</a><span class="current">2 / 6</span><a class="next" href="/lab/bookstore/page/3/">下一页</a></nav>可以看到,翻页栏里有「上一页」,有当前是第几页,最后是一个 class 为 next 的链接。这个 a.next ,就是下一页的链接。
有了它,翻页的思路就清楚了:
- 取回当前页,把书取出来。
- 找
a.next。找到了,就补全地址,作为下一轮要取的网页。找不到,说明已经到了最后一页,就停下来。
把这个思路写成代码,从首页开始一页一页往下取,顺便数一数一共取到了几本书:
import time
from urllib.parse import urljoin
import requests
from bs4 import BeautifulSoup
url = 'https://learn-py.org/lab/bookstore/'
count = 0
while url:
soup = BeautifulSoup(requests.get(url).text, 'html.parser')
count = count + len(soup.select('article.book-card'))
print(url, count)
next_link = soup.select_one('a.next')
url = urljoin(url, next_link['href']) if next_link else None
time.sleep(0.2)
print(f'一共 {count} 本书')输出的结果:
https://learn-py.org/lab/bookstore/ 20
https://learn-py.org/lab/bookstore/page/2/ 40
https://learn-py.org/lab/bookstore/page/3/ 60
https://learn-py.org/lab/bookstore/page/4/ 80
https://learn-py.org/lab/bookstore/page/5/ 100
https://learn-py.org/lab/bookstore/page/6/ 120
一共 120 本书6 页都取到了,一共 120 本书。那么,这个循环是怎么知道该停下来的呢?关键在 while url: ,和循环里找 next_link 的那两行:
while url:的意思是:只要url不是None,就接着取。- 每取完一页,就去找
a.next。找到了,url就换成下一页的地址;找不到,url就变成None。 - 到了第 6 页,找不到
a.next,url变成了None,循环自然就停了。
这种写法还有一个好处:不用事先知道一共有几页。网站以后加了新的一页,也照样能取到。
下面先做一道练习:从第 4 页开始,顺着「下一页」一直翻到最后。
从第 4 页开始,顺着「下一页」链接(a.next )一页一页往下翻,直到最后一页。每取一页,打印这一页的地址和书的数量,并停 0.2 秒;最后打印一共取到几本。
运行后的输出应该是:
https://learn-py.org/lab/bookstore/page/4/ 20
https://learn-py.org/lab/bookstore/page/5/ 20
https://learn-py.org/lab/bookstore/page/6/ 20
共 60 本推荐一个,全网最全最友好的中文 AI 编程指南: coding.stormzhang.ai