1. BeautifulSoup
处理数据分为两步:解析数据和提取数据。处理数据需要用到一个第三方库:BeautifulSoup,可以通过在命令行中输入pip install beautifulsoup4来进行安装。
1) 解析数据
以豆瓣读书Top250为例,将其网页源代码解析为BeautifulSoup对象:
import requests
from bs4 import BeautifulSoup
headers = {
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36'
}
res = requests.get('https://book.douban.com/top250', headers = headers)
soup = BeautifulSoup(res.text, 'html.parser')
Note: 添加headers参数是为了应对反爬虫机制。
创建BeautifulSoup对象时需要传入两个参数,第一个参数是要解析的HTML文本,第二个参数是解析HTML的解析器,html.parser是Python中内置的解析器。
BeautifulSoup对象打印出来虽与网页源代码一样,但它有许多强大的方法和属性,可以方便快捷地提取出需要的数据。
2) 提取数据
BeautifulSoup对象里的find()和find_all()是提取数据最常用的两个方法。find():返回符合条件的首个数据
find_all():返回符合条件的所有数据
它们的用法是传入HTML标签名称,返回符合该HTML标签的数据。除此之外,他们还支持传入HTML属性进行筛选,返回符合条件的数据。例如:
# 查找id='doubanapp-tip'的div标签
soup.find('div', id='doubanapp-tip')
# 查找所有class='rating_nums'的span标签
soup.find_all('span', class_='rating_nums')
通过id、class等HTML属性的筛选,可以快速准确地找到需要的数据。当一个条件无法精确定位时,也可以传入多个HTML属性进行筛选,返回同时符合这些条件的数据。
3) Tag对象
BeautifulSoup将HTML中的元素封装成了Tag对象。和BeautifulSoup对象一样,Tag对象也有find()和find_all()方法。因此,可以通过不断调用这两个方法,一层一层地找到需要的数据。
# 找到所有class='item'的div标签
items = soup.find_all('div', class_='item')
for i in items:
# 找到class_='item'的div标签中的a标签
print(i.find('a'))
这样,就找到了所有书名的数据。但此时返回的还是Tag对象,如果只想要书名和对应的链接,则可以用Tag对象的text属性和HTML属性名取值。
items = soup.find_all('div', class_='item')
for i in items:
tag = i.find('a')
# 获取text属性
name = tag.text
# 获取href属性值
link = tag['href']
print(name, link)
Tag对象常用的属性和方法:tag.find():返回符合条件的首个数据
tag.find_all():返回符合条件的所有数据
tag.text:获取标签的文本内容
tag['属性名']:获取标签HTML属性的值
4) CSS选择器
在CSS选择器中,#代表id,.代表class,比如:#login表示id='login'的所有元素,.item表示class='item'的所有元素。也可以和标签名组合在一起,比如:a#login表示所有id='login'的a元素,p.item表示所有class='item'的p元素,#login.item表示所有id='login'且class='item'的元素,.item.book表示所有class同时为item和book的元素。
子元素选择:当两个选择器之间加了空格,如:.item .book,表示选择所有嵌套在class='item'的元素里面class='book'的元素。如果只需要直接嵌套在第一层符合条件的元素,可以用>分隔,比如:.item > .book。
BeautifulSoup对象中有一个select()方法,将CSS选择器传进去可以直接找到所需的元素,比如上一部分的代码可以简化为:
items = soup.select('div.item a')
for i in items:
name = i.text
link = i['href']
print(name, link)
2. 爬虫进阶
1) 爬取整个网站
前面介绍的方法只能爬取一个页面的数据,为了爬取整个网站,首先要观察网址的查询字符串。以豆瓣读书为例,点击第二页后,网址变成了https://book.douban.com/top250?start=25,?start=25这一部分就是查询字符串,其格式是?key1=value1&key2=value2。查询字符串作为用于搜索的参数或处理的数据传送给服务器处理。
url = 'https://book.douban.com/top250?start={}'
urls = [url.format(num * 25) for num in range(10)]
print(urls)
通过观察每一页网址的规律,可以推测出start的计算公式,用如上代码即可自动生成所有数据的地址。
获取所有页面的地址后,将一个页面的爬虫代码封装成函数,唯一会变的网页地址作为参数,再用for循环遍历所有的网页地址,依次传入封装好的函数,即可实现整个网站的爬取。
import requests
import time
from bs4 import BeautifulSoup
def get_douban_books(url):
headers = {
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36'
}
res = requests.get(url, headers=headers)
soup = BeautifulSoup(res.text, 'html.parser')
items = soup.find_all('div', class_='pl2')
for i in items:
tag = i.find('a')
name = tag['title']
link = tag['href']
print(name, link)
url = 'https://book.douban.com/top250?start={}'
urls = [url.format(num * 25) for num in range(10)]
for item in urls:
get_douban_books(item)
# 暂停1秒防止访问太快被封
time.sleep(1)
2) 反爬虫
反爬虫是网站限制爬虫的一种策略,让爬虫在网站可接受的范围内爬取数据,不至于导致网站瘫痪无法运行。
①判别身份
有些网站在识别出爬虫后,会拒绝爬虫的访问,如果用爬虫直接爬取它,则输出结果为空。这是由于服务器会通过请求头(requests headers)里的信息来判别访问者的身份。
查看网站请求头的方法:在开发者工具中点击Network标签
点击第一个请求
找到Request Headers
找到user-agent字段
为了将爬虫伪装成浏览器,避免被服务器识别出来,需要在requests中定制请求头:定义一个字典,请求头字段作为键,字段内容作为值,传递给headers参数。大部分情况下,只需添加user-agent字段即可。
②IP限制
当爬取大量数据时,由于访问过于频繁,可能会被限制IP。因此,常使用time.sleep()来降低访问的频率。
此外,也可以使用代理来解决IP限制的问题,以便快速地大规模爬取数据。和headers一样,定义一个字典,将http和https这两种协议作为键,对应的IP代理作为值,最后将整个字典作为proxies参数传递给requests.get()方法。
爬取大量数据时需要很多的IP进行切换,因此,需要建立一个IP代理池(列表),每次从中随机选择一个传给proxies参数:
# IP代理池
proxies_list = [
{
"http": "……",
"https": "……"
},
{
"http": "……",
"https": "……"
},
]
for i in urls:
# 从IP代理池中随机选择一个
proxies = random.choice(proxies_list)
get_douban_books(i, proxies)
3) robots.txt
robots.txt是一种存放于网站根目录下的文本文件,用于告诉爬虫此网站中的哪些内容是不应被爬取的,哪些是可以被爬取的。在网站域名后加上/robots.txt即可查看。爬取网站时应遵守规则。
Reference:
[1] Wes McKinney. 2017. Python for Data Analysis: Data Wrangling with Pandas, NumPy, and IPython, 2nd Edition [M]. O'Reilly Media, Inc.
[2] CSDN博客. Python博客[OL]. https://www.csdn.net/nav/python. 2020.