欢迎大家来到“Python从零到壹”,在这里我将分享约200篇Python系列文章,带大家一起去学习和玩耍,看看Python这个有趣的世界。所有文章都将结合案例、代码和作者的经验讲解,真心想把自己近十年的编程经验分享给大家,希望对您有所帮助,文章中不足之处也请海涵。Python系列整体框架包括基础语法10篇、网络爬虫30篇、可视化分析10篇、机器学习20篇、大数据分析20篇、图像识别30篇、人工智能40篇、Python安全20篇、其他技巧10篇。您的关注、点赞和转发就是对秀璋最大的支持,知识无价人有情,希望我们都能在人生路上开心快乐、共同成长。
前一篇文章讲述了BeautifulSoup技术,它是一个可以从HTML或XML文件中提取数据的Python库,一个分析HTML或XML文件的解析器,包括安装过程和基础语法。这篇文章将详细讲解 BeautifulSoup 爬取豆瓣TOP250电影,通过案例的方式让大家熟悉Python网络爬虫,同时豆瓣TOP250也是非常适合入门的案例,也能普及简单的预处理知识。 希望对您有所帮助,本文参考了作者CSDN的文章,链接如下:
同时,作者新开的“娜璋AI安全之家”将专注于Python和安全技术,主要分享Web渗透、系统安全、人工智能、大数据分析、图像识别、恶意代码检测、CVE复现、威胁情报分析等文章。虽然作者是一名技术小白,但会保证每一篇文章都会很用心地撰写,希望这些基础性文章对你有所帮助,在Python和安全路上与大家一起进步。
前文赏析:
前文作者详细介绍了BeautifulSoup技术,这篇文章主要结合具体实例进行深入分析,讲述一个基于BeautifulSoup技术的爬虫,爬取豆瓣排名前250部电影的信息,主要内容包括:
本文从实战出发,让读者初步了解分析网页结构方法并调用BeautifulSoup技术爬取网络数据,后面章节将进一步深入讲解。
豆瓣(Douban)是一个社区网站,创立于2005年3月6日。该网站以书影音起家,提供关于书籍、电影、音乐等作品的信息,其作品描述和评论都是由用户提供(User-Generated Content,简称UGC),是Web 2.0网站中具有特色的一个网站。该网站提供了书影音推荐、线下同城活动、小组话题交流等多种服务功能,致力于帮助都市人群发现生活中有用的事物。
本文主要介绍BeautifulSoup技术爬取豆瓣电影排名前250名的电影信息。第一部分将介绍分析网页DOM树结构。爬取豆瓣的地址为:
上图中显示了豆瓣热门的250部电影的信息,包括电影中文名称、英文名称、导演、主演、评分、评价数等信息,接下来需要对其进行DOM树结构分析。HTML网页是以标签对的形式出现,如< html >< /html >、< div >< /div >等,这种标签对呈树形结构显示,通常称为DOM树结构。
在得到一个网页之后,我们需要结合浏览器对其进行元素分析。比如豆瓣电影网站,选中第一部电影《肖申克的救赎》,右键鼠标“检查”(Chrome浏览器称为“检查”,其他浏览器可能称为“审查元素”等),如下图2所示。
显示结果如图3所示,可以发现它是在< div class=”article” >< /div >路径下,由很多个< li >< /li >组成,每一个< li >< /li >分别对应一部电影的信息。其中,电影《肖申克的救赎》HTML中对应内容为:
<li><div class="item">......div>li>
通过class值为“item”可以定位电影的信息。调用BeautifulSoup扩展包的find_all(attrs={“class”:“item”}) 函数可以获取其信息。
对应的HTML部分代码如下:
<li><div class="item">
<div class="pic">
<em class="">1em>
<a href="https://movie.douban.com/subject/1292052/">
a>
div>
<div class="info">...div>
div>li>
下面通过Python3代码可以获取电影的信息,调用BeautifulSoup中的find_all()函数获取< div class=’item’ >的信息,其结果如图4所示。
test01.py
# -*- coding:utf-8 -*-
# By:Eastmount CSDN
import urllib.request
import re
from bs4 import BeautifulSoup
# 爬虫函数
def crawl(url, headers):
page = urllib.request.Request(url, headers=headers)
page = urllib.request.urlopen(page)
contents = page.read()
#print(contents)
soup = BeautifulSoup(contents, "html.parser")
print('豆瓣电影250: 序号 \t影片名\t 评分 \t评价人数')
for tag in soup.find_all(attrs={
"class":"item"}):
content = tag.get_text()
content = content.replace('\n','') #删除多余换行
print(content, '\n')
# 主函数
if __name__ == '__main__':
url = 'http://movie.douban.com/top250?format=text'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) \
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'}
crawl(url, headers)
运行结构如图4所示,爬取了豆瓣Top250的第一页电影的信息,包括序号、影片名、导演及主演信息、评分、评价人数等。
注意:urllib.error.HTTPError: HTTP Error 418
如果直接使用urllib.request.urlopen(url)会提示该错误,这是因为爬虫被拦截,需要模拟浏览器访问,这里可以打开浏览器按下F12,找到对应Headers内容,然后在Python代码中设置User-Agent即可模拟该浏览器请求。
urlopen()函数:
read()函数:
BeautifulSoup函数:
find_all()函数:
通过前一部分我们获取了电影的简介信息,但是这些信息是融合在一起的,而在数据分析时,通常需要将某些具有使用价值的信息提取出来,并存储至数组、列表或数据库中,比如电影名称、演员信息、电影评分等特征。
作者简单归纳了两种常见的方法:
本部分将结合BeautifulSoup技术,采用节点定位方法获取具体的值。点开HTML网页,检查对应的< li >< /li >节点,可以看到该电影的构成情况,再定位节点内容,如< span class=“title” >节点可以获取标题,< div class=“star” >节点可以获取电影评分和评价人数。
获取节点的核心代码如下,定位class属性为“item”的div布局后,再调用find_all()函数查找class属性为title的标签,并获取第一个值输出,即title[0]。接着调用find()函数爬取评分信息,通过get_text()函数获取内容。
for tag in soup.find_all(attrs={
"class":"item"}):
title = tag.find_all(attrs={
"class":"title"}) #电影名称
info = tag.find(attrs={
"class":"star"}).get_text() #爬取评分和评论数
print(title[0])
print(info.replace('\n',''))
# 肖申克的救赎
# 9.72279813人评价
讲到这里,我们第一页的25部电影信息就爬取成功了,而该网页共10页,每页显示25部电影,如何获取这250部完整的电影信息呢?这就涉及到链接跳转和网站的翻页分析。网站的翻页分析通常有四种方法:
本文主要采用第一种分析方法,后面讲述Selenium技术时,会介绍鼠标模拟点击事件操作的跳转方法。
通过点击图6中的第2页、第3页、第10页,我们可以看到网页URL的变化如下。
第2页URL:https://movie.douban.com/top250?start=25&filter=
第3页URL:https://movie.douban.com/top250?start=50&filter=
第10页URL:https://movie.douban.com/top250?start=225&filter=
它是存在一定规律的,top250?start=25表示获取第2页(序号为26到50号)的电影信息;top250?start=50表示获取第3页(序号为51到75号)的电影信息,依次类推,我们写一个循环即可获取完整的250部电影信息。核心代码如下:
i = 0
while i<10:
num = i*25 #每次显示25部 URL序号按25增加
url = 'https://movie.douban.com/top250?start=' + str(num) + '&filter='
crawl(url)
i = i + 1
注意:当i初始值为0,num值为0,获取第1页信息;当i增加为1,num值为25,获取第2页信息;当i增加为9,num值为225,获取第10页的信息。
讲到这里,爬取豆瓣网电影信息的DOM树结构分析、网页链接跳转已经分析完成,下一小节是讲解完整的代码。
完整代码为test02.py文件,如下所示。
test02.py
# -*- coding: utf-8 -*-
# By:Eastmount CSDN
import urllib.request
import re
from bs4 import BeautifulSoup
import codecs
#-------------------------------------爬虫函数-------------------------------------
def crawl(url, headers):
page = urllib.request.Request(url, headers=headers)
page = urllib.request.urlopen(page)
contents = page.read()
soup = BeautifulSoup(contents, "html.parser")
infofile.write("")
print('爬取豆瓣电影250: \n')
for tag in soup.find_all(attrs={
"class":"item"}):
#爬取序号
num = tag.find('em').get_text()
print(num)
infofile.write(num + "\r\n")
#电影名称
name = tag.find_all(attrs={
"class":"title"})
zwname = name[0].get_text()
print('[中文名称]', zwname)
infofile.write("[中文名称]" + zwname + "\r\n")
#网页链接
url_movie = tag.find(attrs={
"class":"hd"}).a
urls = url_movie.attrs['href']
print('[网页链接]', urls)
infofile.write("[网页链接]" + urls + "\r\n")
#爬取评分和评论数
info = tag.find(attrs={
"class":"star"}).get_text()
info = info.replace('\n',' ')
info = info.lstrip()
print('[评分评论]', info)
#获取评语
info = tag.find(attrs={
"class":"inq"})
if(info): #避免没有影评调用get_text()报错
content = info.get_text()
print('[影评]', content)
infofile.write(u"[影评]" + content + "\r\n")
print('')
#-------------------------------------主函数-------------------------------------
if __name__ == '__main__':
#存储文件
infofile = codecs.open("Result_Douban.txt", 'a', 'utf-8')
#消息头
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) \
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'}
#翻页
i = 0
while i<10:
print('页码', (i+1))
num = i*25 #每次显示25部 URL序号按25增加
url = 'https://movie.douban.com/top250?start=' + str(num) + '&filter='
crawl(url, headers)
infofile.write("\r\n\r\n")
i = i + 1
infofile.close()
运行结果如图7所示,爬取了电影名称、网页连接、评分评论数和影评等信息。
并且将爬取的250部电影信息存储到“Result_Douban.txt”文件中,如下图所示。
在代码中,主函数定义循环依次获取不同页码的URL,然后调用crawl(url)函数对每页的电影信息进行定向爬取。在crawl(url)函数中,通过urlopen()函数访问豆瓣电影网址,然后调用BeautifulSoup函数进行HTML分析,前面第一部分讲解了每部电影都位于< li >< div class=“item” >…< /div >< /li >节点下,故采用如下for循环依次定位到每部电影,然后再进行定向爬取。
for tag in soup.find_all(attrs={
"class":"item"}):
#分别爬取每部电影具体的信息
具体方法如下。
(1) 获取序号
序号对应的HTML源码如图8所示,需要定位到< em class >1< /em >节点,通过find(‘em’)函数获取具体的内容。
对应的代码如下:
num = tag.find('em').get_text()
print(num)
(2) 获取电影名称
电影名称对应的HTML源码如图9所示,包括class='title’对应中文名称和英文名称,class='other’对应电影其他名称。
对应的代码如下,因为HTML中包含两个title,即< span class=‘title’ >< /span >,所以使用下面的函数获取两个标题:
但这里我们仅需要中文标题,则直接通过变量name[0]获取其第一个值,即为中文名称,再调用get_text()函数获取其内容。
name = tag.find_all(attrs={
"class":"title"})
zwname = name[0].get_text()
print('[中文名称]', zwname)
infofile.write("[中文名称]" + zwname + "\r\n")
同时,上述代码调用codecs库进行了文件处理,其中文件操作的核心代码如下,打开文件三个参数分别是:文件名、读写方式、编码方式,此处文件名为“Result_Douban.txt”,采用文件写方式(a),编码方式是utf-8。
infofile = codecs.open("Result_Douban.txt", 'a', 'utf-8') #打开文件
infofile.write(num+" "+name+"\r\n") #写文件
infofile.close() #关闭文件
3.获取电影链接
电影链接对应的HTML源码如上图9所示,定位到< div class=‘hd’ >节点下的< a >< /a >节点,然后获取属性位href的值,即:attrs[‘href’]。
url_movie = tag.find(attrs={
"class":"hd"}).a
urls = url_movie.attrs['href']
print('[网页链接]', urls)
获取评分和内容的方法一样,调用函数即可获取:
但是存在一个问题,它输出的结果将评分数和评价数放在了一起,如“9.4 783221人评价”,而通常在做分析的时候,我们是将评分数存在一个变量中,评价数存在另一变量中。
这就需要进行简单的文本处理,这里推荐大家使用前面讲述过的正则表达式来处理,将此段代码修改如下,调用re.compile(r’\d+.?\d*’)获取字符串中的数字。第一个数字为电影的分数,第二个数字为评论数。
#爬取评分和评论数
info = tag.find(attrs={
"class":"star"}).get_text()
info = info.replace('\n',' ')
info = info.lstrip()
print(info)
mode = re.compile(r'\d+\.?\d*') #正则表达式获取数字
print(mode.findall(info))
i = 0
for n in mode.findall(info):
if i==0:
print('[分数]', n)
infofile.write("[分数]" + n + "\r\n")
elif i==1:
print('[评论]', n)
infofile.write(u"[评论]" + n + "\r\n")
i = i + 1
获取的结果前后对比如图10所示。
这样,整个豆瓣250部电影信息就爬取成功了。接下来,我们再继续深入,去到具体的每个网页中,爬取详细信息及更多的评论。同时,作者更推崇的是本文讲解的分析方法,只有知道了具体的方法才能解决具体的问题。
在第二部分我们详细分析了如何爬取豆瓣前250部电影信息,同时爬取了每部电影对应详细页面的超链接。本小节主要结合每部电影的超链接url网站,定位到具体的电影页面,进行深一步的详情页面爬取。这里作者还是采用电影《肖申克的救赎》举例,前面爬取了该电影的超链接地址为:
该网页打开如图11所示。
作者主要分析如何爬取该部电影的导演信息、电影简介信息以及热门影评信息,其中影评信息如图12所示。
1.爬取详情页面基本信息
下面对详情页面进行DOM树节点分析,其基本信息位于< div class=‘article’ >…< /div >标签下,核心内容位于该节点下的子节点中,即< div id=‘info’ >…< /div >。使用如下代码获取内容:
info = soup.find(attrs={
"id":"info"})
print(info.get_text())
2.爬取详情页面电影简介
同样,通过浏览器审查元素,可以得到如图14所示的电影简介HTML源码,其电影简介位于< div class=‘related-info’ >…< /div >节点下,它包括简短版(short)的简介和隐藏的详细版简介(all_hidden),这里作者通过下列函数获取。代码replace(’\n’,’’).replace(’ ‘,’’)用于过滤所爬取HTML中多余的空格和换行符号。
other = soup.find(attrs={
"class":"related-info"}).get_text()
print other.replace('\n','').replace(' ','') #过滤空格和换行
3.爬取详情页面电影热门评论信息
热门评论信息位于< div id=‘hot-comments’ >…< /div >节点下,然后获取节点下的多个class属性为“comment-item”的div布局,如下图所示。在使用find()或find_all()函数进行爬取时,需要注意标签属性是class还是id,或是其它,必须与之对应一致,才能正确爬取。
#评论
print('\n评论信息:')
for tag in soup.find_all(attrs={
"id":"hot-comments"}):
for comment in tag.find_all(attrs={
"class":"comment-item"}):
com = comment.find("p").get_text() #爬取段落p
print com.replace('\n','').replace(' ','')
完整代码如下:
test03.py
# -*- coding: utf-8 -*-
# By:Eastmount CSDN
import urllib.request
import re
from bs4 import BeautifulSoup
import codecs
#-----------------------------------爬取详细信息-------------------------------------
def getInfo(url, headers):
page = urllib.request.Request(url, headers=headers)
page = urllib.request.urlopen(page)
content = page.read()
soup = BeautifulSoup(content, "html.parser")
#电影简介
print('电影简介:')
info = soup.find(attrs={
"id":"info"})
print(info.get_text())
other = soup.find(attrs={
"class":"related-info"}).get_text()
print(other.replace('\n','').replace(' ',''))
#评论
print('\n评论信息:')
for tag in soup.find_all(attrs={
"id":"hot-comments"}):
for comment in tag.find_all(attrs={
"class":"comment-item"}):
com = comment.find("p").get_text()
print(com.replace('\n','').replace(' ',''))
print("\n\n\n----------------------------------------------------------------")
#-------------------------------------爬虫函数-------------------------------------
def crawl(url, headers):
page = urllib.request.Request(url, headers=headers)
page = urllib.request.urlopen(page)
contents = page.read()
soup = BeautifulSoup(contents, "html.parser")
for tag in soup.find_all(attrs={
"class":"item"}):
#爬取序号
num = tag.find('em').get_text()
print(num)
#电影名称
name = tag.find_all(attrs={
"class":"title"})
zwname = name[0].get_text()
print('[中文名称]', zwname)
#网页链接
url_movie = tag.find(attrs={
"class":"hd"}).a
urls = url_movie.attrs['href']
print('[网页链接]', urls)
#爬取评分和评论数
info = tag.find(attrs={
"class":"star"}).get_text()
info = info.replace('\n',' ')
info = info.lstrip()
#正则表达式获取数字
mode = re.compile(r'\d+\.?\d*')
i = 0
for n in mode.findall(info):
if i==0:
print('[电影分数]', n)
elif i==1:
print('[电影评论]', n)
i = i + 1
#获取评语
getInfo(urls, headers)
#-------------------------------------主函数-------------------------------------
if __name__ == '__main__':
#消息头
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) \
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'}
#翻页
i = 0
while i<10:
print('页码', (i+1))
num = i*25 #每次显示25部 URL序号按25增加
url = 'https://movie.douban.com/top250?start=' + str(num) + '&filter='
crawl(url, headers)
i = i + 1
其中爬取的《龙猫》电影信息输出如图16所示。
讲到这里,使用BeautifulSoup技术分析爬取豆瓣电影前250部电影信息的实例已经讲解完毕,但在实际爬取过程中可能由于某些页面不存在会导致爬虫停止,这时需要使用异常语句“try-except-finally”进行处理。
同时,爬取过程中需要结合自己所需数据进行定位节点,存储至本地文件中,也需要结合字符串处理过滤一些多余的空格或换行。
在学习网络爬虫之前,读者首先要掌握分析网页节点、审查元素定位标签,甚至是翻页跳转、URL分析等知识,然后才是通过Python、Java或C#实现爬虫的代码。本文作者结合自己多年的网络爬虫开发经验,深入讲解了BeautifulSoup技术网页分析并爬取了豆瓣电影信息,读者可以借用本章的分析方法,结合BeautifulSoup库爬取所需的网页信息,并学会分析网页跳转,尽可能爬取完整的数据集。
同时,本章所爬取的内容是存储至TXT文件中的,读者也可以尝试着存储至Excel、CSV、Json文件中,甚至存储至数据库,这将为您后面的数据分析提供强大的数据支撑,数据处理起来更为方便。那么,Python究竟怎么将所爬取的文本存储至数据库呢?后续作者将带给您答案。
该系列所有代码下载地址:
2020年在github的绿瓷砖终于贴完了第一年提交2100余次,获得1500多+stars,开源93个仓库,300个粉丝。挺开心的,希望自己能坚持在github打卡五年,督促自己不断前行。简单总结下,最满意的资源是YQ爆发时,去年2月分享的舆情分析和情感分析,用这系列有温度的代码为武汉加油;最高赞的是Python图像识别系列,也获得了第一位来自国外开发者的贡献补充;最花时间的是Wannacry逆向系列,花了我两月逆向分析,几乎成为了全网最详细的该蠕虫分析;还有AI系列、知识图谱实战、CVE复现、APT报告等等。当然也存在很多不足之处,希望来年分享更高质量的资源,也希望能将安全和AI顶会论文系列总结进来,真诚的希望它们能帮助到大家,感恩有你,一起加油~
最后,真诚地感谢您关注“娜璋之家”公众号,感谢CSDN这么多年的陪伴,会一直坚持分享,希望我的文章能陪伴你成长,也希望在技术路上不断前行。文章如果对你有帮助、有感悟,就是对我最好的回报,且看且珍惜!2020年8月18日建立的公众号,再次感谢您的关注,也请帮忙宣传下“娜璋之家”,哈哈~初来乍到,还请多多指教。
(By:娜璋之家 Eastmount 2021-02-17 夜于贵阳 https://blog.csdn.net/Eastmount )
参考文献如下: