说明:我在写这篇博客时有点着急,前半部分的代码都没有行号,后半部分的代码有行号,不要错把行号看成是代码(应该没有人会犯这种错误)。后面大半部分都是数据的截图,可以直接忽略。
把总结写在前面:不得不说,爬虫真的是一个抓取网页信息的好手段,但是它的局限性很大,Web 信息的巨大容量使得爬虫在给定时间内只能下载少量网页,即使能够提取全部页面,也没有足够的空间来存储。爬行效率低,无法在单位时间内尽可能多的获取高质量页面。
1.首先确定爬取数据的网址,这里我是在谷歌中打开当当网“图书畅销榜”,网址:http://bang.dangdang.com/books/bestsellers/01.00.00.00.00.00-recent30-0-0-1-1
此时显示的是第一页,第二页网址的最后一个数字为2,以此类推。所以,爬取所有页码只需使用for语句构造循环,按页码依次进行网址的拼接并发起请求即可
演示代码:
import requests import pandas as pd from bs4 import BeautifulSoup headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36'} #模拟浏览器的身份验证信息 for i in range(1,26): url = f'http://bang.dangdang.com/books/bestsellers/01.00.00.00.00.00-recent30-0-0-1-{i}'#循环生成的每一页网址
2.经确认,数据是动态加载的,可以用响应对象的text属性获取网页源代码,再从源代码中提取数据。在上述代码的for语句构造的循环内部继续添加如下代码:
response=requests.get(url=url,headers=headers,timeout=10)#对25页的不同网址循环发起请求,设置超时等待为10秒 html_content=response.text#获取网页源代码 soup=BeautifulSoup(html_content,'lxml')#将网页源代码实比例转化为BeautifulSoup对象 parse_html(soup)#调用自定义函数提取BeautifulSoup对象中的数据,具体代码见后 print(f'第{i}页爬取完毕')
3.分析要提取数据的标签。要提取的排行榜数据位于class属性值为bang_list的<ul>标签下的多个<li>标签中。
4.编写提取数据的代码。
创建字典data_info,编写自定义函数parse_html(),从BeautifulSoup对象中提取数据并添加到字典中。
演示代码:
data_info = {'图书排名': [], '图书名称': [], '图书作者': [], '图书出版时间': [], '图书出版社': [], '图书价格': []}#新建一个空字典
def parse_html(soup):#解析每一个BeautifulSoup对象 li_list = soup.select('.bang_list li') # 通过层级选择器定位class属性值为bang_list的标签下的所有<li>标签 for li in li_list:#将从每一个<li>标签中解析到的数据添加到字典data_info相应键的对应列表中 data_info['图书排名'].append(li.select('.list_num ')[0].text.replace('.', '')) data_info['图书名称'].append(li.select('.name a')[0].text) data_info['图书作者'].append(li.select('.publisher_info ')[0].select('a')[0].text) data_info['图书出版时间'].append(li.select('.publisher_info span')[0].text) data_info['图书出版社'].append(li.select('.publisher_info ')[1].select('a')[0].text) data_info['图书价格'].append(float(li.select('.price .price_n')[0].text.replace('¥', '')))
注意:在最终的代码文件中,定义parse_html()函数的代码需位于调用该函数的代码之前。
5.缺失值和重复值的处理。
先用pandas模块将字典转换为DataFrame对象格式,再判断缺失值和重复值。
演示代码:
1 book_info=pd.DataFrame(data_info) 2 print(book_info.isnull())#缺失值判断 3 print(book_info.duplicated())#重复值判断
6.一异常值的处理。本文只研究价格在100元以下的图书,所以需要将价格高于100元的图书删除。
演示代码:
1 book_info['图书价格'][book_info['图书价格']>100]=None#将大于100的图书价格替换为空值 2 book_info=book_info.dropna()#删除有空值的一行数据
7.保存爬取的数据,储存为csv文件。
演示代码:
1 book_info.to_csv('当当网图书销售排行.csv',enco ding='utf-8',index=False)
完整代码在这里:
1 import requests 2 import pandas as pd 3 from bs4 import BeautifulSoup 4 headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36'} 5 6 data_info = {'图书排名': [], '图书名称': [], '图书作者': [], '图书出版时间': [], '图书出版社': [], '图书价格': []} 7 def parse_html(soup): 8 li_list = soup.select('.bang_list li') 9 for li in li_list: 10 data_info['图书排名'].append(li.select('.list_num ')[0].text.replace('.', '')) 11 data_info['图书名称'].append(li.select('.name a')[0].text) 12 data_info['图书作者'].append(li.select('.publisher_info ')[0].select('a')[0].text) 13 data_info['图书出版时间'].append(li.select('.publisher_info span')[0].text) 14 data_info['图书出版社'].append(li.select('.publisher_info ')[1].select('a')[0].text) 15 data_info['图书价格'].append(float(li.select('.price .price_n')[0].text.replace('¥', ''))) 16 17 for i in range(1, 26): 18 url = f'http://bang.dangdang.com/books/bestsellers/01.00.00.00.00.00-recent30-0-0-1-{i}' 19 response = requests.get(url = url, headers = headers, timeout = 10) 20 html_content = response.text 21 soup = BeautifulSoup(html_content, 'lxml') 22 parse_html(soup) 23 print(f'第{i}页爬取完毕') 24 25 book_info = pd.DataFrame(data_info) 26 print(book_info.isnull()) 27 print(book_info.duplicated()) 28 29 book_info['图书价格'][book_info['图书价格'] > 100] = None 30 book_info = book_info.dropna() 31 32 book_info.to_csv('当当网图书销售排行.csv', encoding = 'utf-8', index = False)
结果如下: