爬虫入门教程⑧— BeautifulSoup解析豆瓣即将上映的电影信息

Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库.它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式.Beautiful Soup会帮你节省数小时甚至数天的工作时间.

这是一个利器,让我们能够从复杂的HTML代码里面,提取出我们我们想要的目标数据。

  • 本章我们会以 爬取豆瓣电影成都即将上映的影片信息 作为案例讲解 BeautifulSoup 的用法。
    我们需要爬取的内容有:
    所有影片的 名字、详情链接、上映时间、影片类型、地区、关注者数量
    网页截图如下:
豆瓣即将上映影片列表

开始本章前,请确认你已经安装Python以及jupyter、requests、lxml和bs4这4个Python包。如果你没有,那请移步前面的章节进行安装操作:


 

  1. 打开jupyter开始写代码
    命令行输入jupyter notebook并回车。如果你设置了默认浏览器,那么会自动打开浏览器进去到你打开cmd的文件夹。然后点击右边的New,Python3,我们就新建了一个Python3的项目了。
    如果你正好看了上一章节,那么也可以使用上次的代码文件,直接打开就好了。
文件编辑首页
  1. requests请求到网页源代码
    运用上一节学到的知识,我们先进行爬取第一步,获取到网页源代码。
    豆瓣电影即将上映的影片的网页的地址是:https://movie.douban.com/cinema/later/chengdu/
    那么我们开始编写代码来获取到这个网页的源代码:
import requests
# 旧版教程
# url = "https://movie.douban.com/cinema/later/chengdu/"
# response = requests.get(url)

# 2019-12-23更新,解决不能获取到响应的问题
url = "https://movie.douban.com/cinema/later/chengdu/"  # URL不变
# 新增伪装成浏览器的header
fake_headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Safari/537.36'
}
response = requests.get(url, headers=fake_headers)  # 请求参数里面把假的请求header加上
print(response.content.decode('utf-8'))
运行效果

我们可以成功看到网页的源代码了,证明网页下载没问题,而且在网页代码之中,可以找到我们需要的电影信息(在输出界面一直往下翻,快到底了就能看到啦~)

  1. 保存网页到本地,方便快速加载
    网页我们拿到了,下面就要进行每一步的调试了
    为了我们能够快速调试自己的代码、给豆瓣服务器减少一点压力,也为了避免因为自己调试过快,被豆瓣封掉,所以我们最好把网页保存到本地。这样我们就能用最短的时间加载到网页,而不用每次调试都去豆瓣请求一下。
import requests
# 旧版教程
# url = "https://movie.douban.com/cinema/later/chengdu/"
# response = requests.get(url)

# 2019-12-23更新,解决不能获取到响应的问题
url = "https://movie.douban.com/cinema/later/chengdu/"  # URL不变
# 新增伪装成浏览器的header
fake_headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Safari/537.36'
}
response = requests.get(url, headers=fake_headers)  # 请求参数里面把假的请求header加上
# 保存网页到本地
file_obj = open('douban.html', 'w')  # 以写模式打开名叫 douban.html的文件
# 如果打开网页显示的是乱码那么就用下一行代码
# file_obj = open('douban.html', 'w', encoding="utf-8")  # 以写模式打开名叫 douban.html的文件,指定编码为utf-8
file_obj.write(response.content.decode('utf-8'))  # 把响应的html内容
file_obj.close()  # 关闭文件,结束写入

恩,Python保存文件,就这么简单。。。
这个时候,你打开jupyter最开始弹出来的页面,可以找到一个douban.html的文件了,点击打开,内容和我们目标网页一模一样,但是浏览器顶端的网址变了。

  1. 读取文件并用BeautifulSoup加载
    我们现在暂时不使用上面的 区块(cell) 了,就让它保留这样子以便后用;我们在下面的新cell,开始键入代码读取文件并加载到BeautifulSoup里面:
from bs4 import BeautifulSoup  # 从bs4引入BeautifulSoup
# 读取文件内容到html变量里面
file_obj = open('douban.html', 'r')  # 以读方式打开文件名为douban.html的文件
html = file_obj.read()  # 把文件的内容全部读取出来并赋值给html变量
file_obj.close()  # 关闭文件对象

soup = BeautifulSoup(html, 'lxml')  # 初始化BeautifulSoup
print(soup)  # 输出BeautifulSoup转换后的内容
运行结果
  • 这里要说明一下,初始化BeautifuSoup的参数。
    第一个参数 html是网页的源代码,可以是个Unicode字符串,也可以是一个二进制字符串(如果第一个参数是字符串并且网页自带了charset信息,BS会默认采用网页的默认编码解码,否则默认以你当前文件执行的编码(通常是utf-8)进行解析。如果是二进制字符串,如果自己手动指定了编码,就以指定编码解析,否则默认utf-8解析)。
    第二个参数 lxml是BeautifulSoup采用的网页解析器,我们安装lxml用处就在这体现出来了。如果不指定,那么默认会采用Python内置的html.parser进行解析。
    还有更多的可用参数在之后进行讲解。

而输出的内容和我们之前的输出似乎是完全一样的,因为我们还没对soup进行操作。

  1. BeautifulSoup的基本使用语法规则

    • .find() 使用示例
      soup.find('a')。那么会返回在soup包含的源代码中,遇到的第一个<a>...</a>标签内容对象。
      soup.find('a', id='next')。那么会返回在soup包含的源代码中,遇到的第一个有属性为id,值为next的<a>对象,比如<a id="next">...</a>。(不只可以用id,大部分其他的属性都可以直接使用,比如src、name。 值得注意的是,class这个属性因为是Python关键字,不能直接使用,所以在BS里面,使用class_='...'进行代替 )
      find返回的结果,依然可以继续使用find()或者find_all()方法。如果找不到指定的内容,find会返回None

    • .find_all()使用示例
      soup.find_all('a')。那么会返回在soup包含的源代码中,遇到的所有<a>...</a>标签内容的可迭代对象(我们可以把它看成一个 list 或者数组)
      soup.find_all('a', class_='next')。那么会返回在soup包含的源代码中,遇到的所有属性为class,值为next的<a>的 可迭代对象,比如<a class="next">...</a>。(语法和find也一样,class也不能直接写)
      find_all返回的“list”中的单个对象 依然可以继续使用find()或者find_all()方法。如果找不到指定的内容,find_all会返回一个空的“list”。

    • 获取元素的某个属性
      soup['src],这样我们就能取出soup对象的src属性了。如果该属性不存在,那么程序会报错。

    • 获取元素中的所有文本
      soup.text,假设soup对象为<div>你好<a>复联</a></div>,那么这个操作返回字符串是你好复联

  2. 分析网页,制订提取内容策略
    这一步非常重要,直接影响了我们能不能提取到我们想要的内容。
    我们返回浏览器打开的豆瓣网页。找到网页中的第一个电影的名字,鼠标指向该名字,点击右键,选择 检查/审查元素,然后便会打开一个新的小窗口在页面上,并且将网页代码中电影的名字显示在了里面,并且你鼠标指向的元素会显示出它的大小,内容会被选中。

    审查元素

    我们同时滑动鼠标的位置,应该会发现
    当鼠标划到图片中的<ul>...</ul>标签的时候,复仇者联盟影片的详细信息被选中了。
    当鼠标划到下一个<div class="item mod odd">...</div>的时候,下一个影片战犬瑞克斯的所有信息被选中了。
    当鼠标划到图片上方的<div id="showing-soon" class="tab-bd">的时候,整个我们需要采集的影片信息都被选中了。

    • 这几个动作告诉了我们的信息有:
      1. 我们需要的内容全都在<div id="showing-soon" class="tab-bd"></div>里面。
      2. 每个影片的信息,都在一个<div class="item mod odd">...</div>或者<div class="item mod">...</div>里面。画面左边的影片没有odd属性,右边的有odd属性(这好像对于我们采集信息没啥用)

    那么我们的策略,就是先找到囊括了所有的影片的div,然后再从这个div里面找到所有的影片的div,最后再从每个影片的div里面解析出来我们需要的名字、链接等等信息。代码就可以稍微往下写一点了。也就开始要运用前面提到的BS的一些基本用法了:

from bs4 import BeautifulSoup  # 从bs4引入BeautifulSoup
# 读取文件内容到html变量里面
file_obj = open('douban.html', 'r')  # 以读方式打开文件名为douban.html的文件
html = file_obj.read()  # 把文件的内容全部读取出来并赋值给html变量
file_obj.close()  # 关闭文件对象

soup = BeautifulSoup(html, 'lxml')  # 初始化BeautifulSoup
# print(soup)  # 输出BeautifulSoup转换后的内容
all_movies = soup.find('div', id="showing-soon")  # 先找到最大的div
# print(all_movies)  # 输出最大的div的内容
for each_movie in all_movies.find_all('div', class_="item"):  # 从最大的div里面找到影片的div
    print(each_movie)  # 输出每个影片div的内容
运行结果
  1. 提取信息
    那么这一步我们需要做的,就是从这个包含了电影所有信息的div里面,提取出我们需要的信息了。先截个图,找到我们的目标们。

    单个div视图

    现在,代码中each_movie这个变量的内容就是截图中的内容。
    目标们的位置:

电影属性 文档中的位置
名字 在第 2 个<a>标签里面
链接 在第 1 个和第 2 个<a>标签的 href 属性里面
上映日期 在第 1 个<li>标签里面
类型 在第 2 个<li>标签里面
地区 在第 3 个<li>标签里面
关注者数量 在第 4 个<li>标签里面

那么我们就可以开始制定策略了。
名字:先获取所有的<a>标签,取第二个<a>text
链接:利用上一步获取到的所有标签,取第一个或者第二个<a>href属性。
上映日期等等我们就先取到所有的<li>标签,依次取出里面的text的值就是我们所需要的目标了。
那就开始写代码了!

from bs4 import BeautifulSoup  # 从bs4引入BeautifulSoup
# 读取文件内容到html变量里面
file_obj = open('douban.html', 'r')  # 以读方式打开文件名为douban.html的文件
html = file_obj.read()  # 把文件的内容全部读取出来并赋值给html变量
file_obj.close()  # 关闭文件对象

soup = BeautifulSoup(html, 'lxml')  # 初始化BeautifulSoup
# print(soup)  # 输出BeautifulSoup转换后的内容
all_movies = soup.find('div', id="showing-soon")  # 先找到最大的div
# print(all_movies)  # 输出最大的div的内容
for each_movie in all_movies.find_all('div', class_="item"):  # 从最大的div里面找到影片的div
    # print(each_movie)  # 输出每个影片div的内容
    all_a_tag = each_movie.find_all('a')  # 找到所有的a标签
    all_li_tag = each_movie.find_all('li')  # 找到所有的li标签
    movie_name = all_a_tag[1].text  # 从第二个a标签的文字内容提取影片名字
    moive_href = all_a_tag[1]['href']  # 从第二个a标签的文字内容提取影片链接
    movie_date = all_li_tag[0].text  # 从第1个li标签的文字内容提取影片上映时间
    movie_type = all_li_tag[1].text
    movie_area = all_li_tag[2].text
    movie_lovers = all_li_tag[3].text
    print('名字:{},链接:{},日期:{},类型:{},地区:{}, 关注者:{}'.format(
        movie_name, moive_href, movie_date, movie_type, movie_area, movie_lovers))

运行效果:


输出效果
  1. 合并 请求网页 与 解析网页 的代码
    合并代码我们就可以去掉保存文件和读取文件的部分啦~,这就是最终版本的代码了。
import requests
from bs4 import BeautifulSoup  # 从bs4引入BeautifulSoup

#请求网页
# 2019-12-23更新,解决不能获取到响应的问题
url = "https://movie.douban.com/cinema/later/chengdu/"  # URL不变
# 新增伪装成浏览器的header
fake_headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; '\
'WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Safari/537.36'
}
response = requests.get(url, headers=fake_headers)  # 请求参数里面把假的请求header加上

# 解析网页
# 初始化BeautifulSoup方法一:利用网页字符串自带的编码信息解析网页
soup = BeautifulSoup(response.content.decode('utf-8'), 'lxml')
# 初始化BeautifulSoup方法二:手动指定解析编码解析网页
# soup = BeautifulSoup(response.content, 'lxml', from_encoding='utf-8')

# print(soup)  # 输出BeautifulSoup转换后的内容
all_movies = soup.find('div', id="showing-soon")  # 先找到最大的div
# print(all_movies)  # 输出最大的div的内容
for each_movie in all_movies.find_all('div', class_="item"):  # 从最大的div里面找到影片的div
    # print(each_movie)  # 输出每个影片div的内容
    all_a_tag = each_movie.find_all('a')
    all_li_tag = each_movie.find_all('li')
    movie_name = all_a_tag[1].text
    moive_href = all_a_tag[1]['href']
    movie_date = all_li_tag[0].text
    movie_type = all_li_tag[1].text
    movie_area = all_li_tag[2].text
    movie_lovers = all_li_tag[3].text
    print('名字:{},链接:{},日期:{},类型:{},地区:{}, 关注者:{}'.format(
        movie_name, moive_href, movie_date, movie_type, movie_area, movie_lovers))


顺便布置个作业: 试着自己把影片海报的链接给爬下来并输出
 

  1. 总结
    本章,我们利用了上一章所学的用 jupyter 编写代码、requests 请求网页的技能。
    新学习了如何从目标网页提取我们需要的信息。需要掌握的是BeautifulSoup的简单使用。
    学会把请求和解析分开来完成,这样代码会更加具有结构性,一个一个模块完成了,最后拼接起来,就是摩天大厦了!
    教程可能会比较长,其实就一个30行的代码,为了讲的详细,所以可能有很多废话。能够认真看到这里的,那肯定是非常有恒心有耐心的人。
    另外,写代码不要老是复制哦,一定要试着自己写,写代码的过程非常重要。

 

本节到此结束,下一节是关于保存爬取的数据的教程。感谢观看。

 


 
传送门:

下一章:

所有的章节:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,123评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,031评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,723评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,357评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,412评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,760评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,904评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,672评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,118评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,456评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,599评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,264评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,857评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,731评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,956评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,286评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,465评论 2 348

推荐阅读更多精彩内容