第0关
练习-文章下载-参考
题目要求:获取文章[《HTTP状态响应码》]全部内容,并且打印出全文内容。
文本URL:
https://localprod.pandateacher.com/python-manuscript/crawler-html/exercise/HTTP%E5%93%8D%E5%BA%94%E7%8A%B6%E6%80%81%E7%A0%81.md
首先调用requests库,使用requests.get('URL')获取文件,返回的是Response对象。
然后需要把Response对象用合适的数据形式返回。
最后存储数据:
存储文件的三个步骤:打开文件,存储文件,关闭文件。
【提示】
这是一个文本,所以应该使用response.text,把Response对象的内容以字符串的形式返回。
【解答】
选择语言
import requests
destnation_url = 'https://localprod.pandateacher.com/python-manuscript/crawler—html/exercise/HTTP%E5%93%8D%E5%BA%94%E7%8A%B6%E6%80%81%E7%A0%81.md'
res = requests.get (destnation_url)
print(res.status_code) # 查看响应码
article=res.text # 把Response对象的内容以字符串的形式返回
print(article)
练习-图片下载-参考
题目要求:
获取下面的图片,并储存图片。
https://res.pandateacher.com/2019-01-12-15-29-33.png
完成存储后,重新刷新页面,即可在【文件】内看到图片。
【提示】
1. 获取数据
图片URL:https://res.pandateacher.com/2019-01-12-15-29-33.png
首先调用requests库,使用requests.get('URL')获取文件,返回的是Response对象。
然后需要把Response对象用合适的数据形式返回。
2. 存储数据
存储文件的三个步骤:打开文件,存储文件,关闭文件。
这是一个图片,所以应该使用response.content,把Response对象的内容以二进制数据的形式返回。
【解答】
选择语言
import requests
res = requests.get('https://res.pandateacher.com/2019-01-12-15-29-33.png')
# 发出请求,并把返回的结果放在变量res中
pic=res.content
# 把Reponse对象的内容以二进制数据的形式返回
photo = open('spider.jpg','wb')
# 新建了一个文件ppt.jpg,这里的文件没加路径,它会被保存在程序运行的当前目录下。
# 图片内容需要以二进制wb读写。你在学习open()函数时接触过它。
photo.write(pic)
# 获取pic的二进制内容
photo.close()
# 关闭文件
练习-音频下载-参考
题目要求:
获取下面的音乐,并且储存它,然后请刷新页面,这样你就可以在【文件】内看到它。音乐URL:
https://static.pandateacher.com/Over%20The%20Rainbow.mp3
1. 获取数据
音乐URL:https://static.pandateacher.com/Over%20The%20Rainbow.mp3
首先调用requests库,使用requests.get('URL')获取文件,返回的是Response对象。
然后需要把Response对象用合适的数据形式返回。
2. 存储文件
存储文件的三个步骤:打开文件,存储文件,关闭文件。
【提示】
这是一个音频,所以应该使用response.content,把Response对象的内容以以二进制数据的形式返回。
【解答】
选择语言
import requests
res=requests.get('https://static.pandateacher.com/Over%20The%20Rainbow.mp3')
# 发出请求,并把返回的结果放在变量res中
mp3=res.content
# 把Reponse对象的内容以二进制数据的形式返回
music = open('rainbow.mp3','wb')
# 新建了一个文件ppt.jpg,这里的文件没加路径,它会被保存在程序运行的当前目录下。
# 图片内容需要以二进制wb读写。你在学习open()函数时接触过它。
music.write(mp3)
# 获取pic的二进制内容
music.close()
# 关闭文件
第1关
练习-我的书苑我作主-参考
复习了所有知识点,一切都准备就绪,那就开始写属于你的网页吧!
我已经把网页的HTML源代码准备好了,你直接在上面修改就好。
现在,请把网页[这个书院不太冷5.0](https://localprod.pandateacher.com/python-manuscript/crawler-html/spider-men5.0.html)修改为你喜欢的模样。
必做:
- 修改网页标题
- 增加至少一本书的描述
- 修改网页底部
选做:
- 修改已有书籍的描述
- 增加多本书的描述
- 自由地在HTML文档上修改任意内容
【提示】
网页结构修改:
为了让网页的结构更加清晰,可以把每一本书都写成一个`<div>`元素。
细节的修改:
书籍封面图片的URL,你可以试试用豆瓣书籍主页的图片。右键点击图片,然后选择【复制图片地址】(https://res.pandateacher.com/2019-01-12-21-30-00.png)
【解答】
你的书苑你做主。
这是我给你的参考答案,你可以点开下面的链接看看:
https://localprod.pandateacher.com/python-manuscript/crawler-html/exercise/01-01-test.html
⚠️注意,修改图片是直接将自己要替换的图片的链接在检查里面双击替换
另外,有的同学在学习第1关课程的时候,会遇见以下情况:
右边空空如也?遇到这种情况怎么办?莫慌!
在本地文件夹新建一个.txt文件,并把后缀改为.html
然后,鼠标右键点击这个文件,选择“打开方式”,再选择“记事本”
把课堂上的html代码复制粘贴过来,并选择左上角的“文件”,再选择“另存为”;
在另存为里,需要将编码格式修改为“utf-8”哦,然后点击“保存”
最后一步,鼠标双击打开 书苑.html 这个文件,就可以得到一个本地的网页啦
第2关
练习-博客爬虫-参考
题目要求:
你需要爬取的是博客【人人都是蜘蛛侠】中,《未来已来(四)——Python学习进阶图谱》的所有文章评论,并且打印。
文章URL:
https://wordpress-edu-3autumn.localprod.oc.forchange.cn/all-about-the-future_04/
【提示】
首先,记得调用requests库和BeautifulSoup模块
然后,按照爬虫的四个步骤来写代码:(不需要写第3步存储数据)
第0步:获取数据
`requests.get()`
第1步:解析数据
`BeautifulSoup(网页源代码的字符串格式,'html.parser')`
第2步:提取数据
`find_all()`
`for`循环遍历`list`
`Tag.text`
第2关-爬取蜘蛛侠网评论
https://wordpress-edu-3autumn.localprod.oc.forchange.cn/all-about-the-future_04/
打开网站,将页面拉到下面的评论区域
没错,我们要爬取的就是这些评论,最重要的就是能够十分精准地定位到评论所在的标签
跟着助教操作吧,选择一条评论,鼠标右键,点击“检查”
此时检查会打开,并在element里定位到该评论所在的标签下,如图
恭喜你,迅速又准确地找到了这个标签的位置,接下来我们需要检查一下,定位到这个标签的话,能否准确的提取出数据?
首先是评论所在的<p>标签。让我们在element里面 ctrl+F ,调出搜索框,直接搜'p’
emmmmm,266个?算了算了(如果你有经验的话,是可以直接判断出p标签定位是肯定行不通的)
那我们往上找一级,<p>标签再上一级是?是 <div' class= 'comment-content'>
来,再搜一搜'comment-content’
嗯哼,搜出来6个,而且仔细观察一下,每一个<div' class= 'comment-content'> 都包含一个评论的内容。没错,这就是我们要找的!
ok,现在应该有个思路了,定位到这个标签,并且提取出标签下的文本内容
我们可以用 .find_all('div',class_= 'comment-content') 定位标签
为什么用find_all(),因为每个评论都是一个独立的标签,而我们需要把所有标签都抓下来,所以要用到find_all()
那要注意的是,find_all()得到的是一个列表,所以find_all()后面是不能直接加.text的哦!
我们需要用到列表循环遍历的方式,把每一个Tag对象先从列表里拿出来,再加上.text提取文本内容
答案是这样的:
选择语言
import requests # 调用requests库
from bs4 import BeautifulSoup # 调用BeautifulSoup库
destnation_url = 'https://wordpress-edu-3autumn.localprod.oc.forchange.cn/all-about-the-future_04/comment-page-545/#comments'
# 把网址复制给变量destnation_url
destnation = requests.get (destnation_url) # 返回一个response对象,赋值给destnation
soup = BeautifulSoup(destnation.text,'html.parser') # 把网页解析为BeautifulSoup对象
comments = soup.find_all('div',class_= 'comment-content') #通过匹配属性提取出我们想要的元素
for comment in comments: # 遍历列表,取出列表中的每一个值
print(comment.text) # 打印评论的文本
同学应该发现,这个代码只能爬取到最后一页的评论内容
别着急嘛,那这个网址只能爬取当前页的内容,那我们看看其他页有没有什么特点呢?
第545页:
https://wordpress-edu-3autumn.localprod.oc.forchange.cn/all-about-the-future_04/comment-page-545/#comments
第544页:
https://wordpress-edu-3autumn.localprod.oc.forchange.cn/all-about-the-future_04/comment-page-544/#comments
第543页:
https://wordpress-edu-3autumn.localprod.oc.forchange.cn/all-about-the-future_04/comment-page-543/#comments
看出来了嘛?变化就是数字。思考一下,如何利用这个数字去实现不同页码的改变?
如果我们假设这个数字是一个变量 x ,那有什么方法可以让这个 x 实现叠加变化?
想到了嘛?用 循环
这里以for循环作为一个例子,要注意url的数据拼接部分哦!
选择语言
import requests # 调用requests库
from bs4 import BeautifulSoup # 调用BeautifulSoup库
for x in range(1,545):
destnation_url = 'https://wordpress-edu-3autumn.localprod.oc.forchange.cn/all-about-the-future_04/comment-page-'+str(x)+'/#comments'
# 把网址复制给变量destnation_url
destnation = requests.get (destnation_url) # 返回一个response对象,赋值给destnation
soup = BeautifulSoup(destnation.text,'html.parser') # 把网页解析为BeautifulSoup对象
comments = soup.find_all('div',class_= 'comment-content') #通过匹配属性提取出我们想要的元素
for comment in comments: # 遍历列表,取出列表中的每一个值
print(comment.text) # 打印评论的文本
练习-书店寻宝-参考
1.第一个小练习
题目要求:你需要爬取的是网上书店(http://books.toscrape.com/)中所有书的分类类型,并且将它们打印出来。网页URL:http://books.toscrape.com/
【提示】
要找到就需要先找到所有的li标签,仔细看HTML源代码的结构,这里需要嵌套提取好几层:
`find('ul',class_='nav').find('ul').find_all('li')`
最终打印结果,可以使用`str.strip()`去除特殊字符串。
比如,使用`.strip()`即可去掉' 我是吴枫\n'文字前面的空格与后面的换行。
http://books.toscrape.com/
第一个小练习,我们要把这网站左侧的各种书籍的分类给提取出来
来,开始。第一步,最便捷的方式,右键点击第一个分类“Travel”,然后选择“检查”
检查会被打开,并且自动定位到element里面的标签位置
简单看一下右边的标签情况,很多<li>标签中,打开的是第一个<li>标签
<li>标签下又是一个<a>标签,那我们把这个<a>标签展开看一看
嗯,没错。Travel这个分类的文本内容就在这个标签下。接着我们继续多看一下几个<li>标签,会发现,每个<li>标签下都是一个分类的文本
那大概的思路是不是有啦?
同学可能会这么想:定位到<li>标签,爬它!
等等等等,别着急
真的以为定位到<li>标签就好了?
再检查一下吧,用ctrl+F搜索一下<li>标签
结果如下
搜出来的第一个<li>标签,不是我们需要的内容。这说明,如果直接定位到<li>标签是不准确的
所以我们还是一步步来定位,不用要求一步到位哦
这里需要提取好几层呢,耐心一点,精准一点
最后,我们可以把程序写出这个样子
选择语言
import requests
from bs4 import BeautifulSoup
res_bookstore = requests.get('http://books.toscrape.com/')
bs_bookstore = BeautifulSoup(res_bookstore.text,'html.parser')
list_kind = bs_bookstore.find('ul',class_='nav').find('ul').find_all('li') # 这里需要提取好几层
for tag_kind in list_kind:
tag_name = tag_kind.find('a')
print(tag_name.text.strip()) # 去除特殊字符串,比如空格,\n,\t等等
这里有三处需要注意:
1、第7行,这里是把所有的标签定位连写,同学们在自己练习的时候,可以分开一步步来写;
2、第7行,最后用的find_all('li’)是把所有<li>标签爬取下来,得到的是一个列表;
3、第12行,tag_name.text是提取出<a>标签的文本部分,.strip() 是将文本部分的特殊字符切除,同学们在练习的时候,可以把这个去掉和加上,看看两者的区别是什么;
2.第二个小练习
题目要求:你需要爬取的是网上书店[Books to Scrape](http://books.toscrape.com/)Travel这类书中,所有书的书名、评分、价格三种信息,并且打印提取到的信息。
网页URL:http://books.toscrape.com/catalogue/category/books/travel_2/index.html
在此,提取书名和评分都有一定难度,想不到怎么做?查看提示。
【提示】
1.提取书名:
需要注意的是,`<a>`标签中的文字内容所显示的不是完整书名,完整的书名是`<a>`标签中,`<title>`属性的值。
因此需要用到`tag['属性名']`来提取属性值。
2.提取评分:
需要注意的是,评分藏在了`<p>`标签的`<class>`属性中,它是`<class>`的第1个属性值。
我们需要根据`<class>`的第0个属性值`<star-rating>`提出它的第1个属性值。
http://books.toscrape.com/
题目要求我们爬取Travel类别下,所有书籍的信息。首先我们可以先看一下,每本书的信息是否有独立的标签包含着,这里,我们找到的一个标签是 <article class='product_pod'>
我们从左边的蓝色阴影也可以看出,这个标签下包含了该本书籍的所有信息,那就是它了,我们需要先把标签定位到这里来
选择语言
import requests
from bs4 import BeautifulSoup
res_bookstore = requests.get('http://books.toscrape.com/catalogue/category/books/travel_2/index.html')
bs_bookstore = BeautifulSoup(res_bookstore.text,'html.parser')
list_books = bs_bookstore.find_all(class_='product_pod')
注意,这里用的是find_all(),是要把每本书都先独立提取出来。接下来,我们再进入每一个<article class='product_pod'>下进行数据的提取
书名提取
可以跟着上一步的标签定位,继续把下面的标签展开看看有什么内容
找到是在<article>标签下的<h3>标签下的<a>标签,这里要提取2层才能找到书名的文本内容哦
仔细观察一下,如果这里提取文本的话,后面是个“...”的省略号,而不是完整的书名信息
那,再仔细观察一下,<a>标签中有一个“title”属性,而这个属性的内容却是完整的书名
ok,既然这样,我们就不提取文本内容了,改为提取<a>标签的“title”属性
还记得怎么提取标签的属性嘛?用的是Tag['属性名']的格式,来看看代码
选择语言
import requests
from bs4 import BeautifulSoup
res_bookstore = requests.get('http://books.toscrape.com/catalogue/category/books/travel_2/index.html')
bs_bookstore = BeautifulSoup(res_bookstore.text,'html.parser')
list_books = bs_bookstore.find_all(class_='product_pod')
for tag_books in list_books:
tag_name = tag_books.find('h3').find('a') # 找到a标签需要提取两次
print(tag_name['title']) # 这里用到了tag['属性名']提取属性值
ok?ok就继续往下,不ok就多看多操作几遍
评分提取
这里重点讲一下评分的提取。首先我们先定位下整个评分的标签是什么。可以用到我们的小箭头工具,然后移到评分的位置,在element会自动定位到标签
这本书有三颗星星,看看左边的标签,<p class='star-rating Three'>,刚好是Three。
再多看几本书后,基本上就可以确定这个标签就是我们要提取的书籍评分内容
这里需要注意了,我们的目的是要提取<p>标签的class属性的值
而这里,class属性是有两个值的,分别是star-rating和Three,中间用空格分开
定位标签时,为了定位准确,我们可以定位所有书都有的<p>标签的class属性:“star-rating”
然后用Tag['属性名']的格式提取出class属性的值。而因为上面说了,class属性是有两个值的,所以返回的是一个列表。我们需要提取的是列表中偏移量为1的值,所以还需要加上[1]来提取
完整写出来是这样滴:
选择语言
import requests
from bs4 import BeautifulSoup
res_bookstore = requests.get('http://books.toscrape.com/catalogue/category/books/travel_2/index.html')
bs_bookstore = BeautifulSoup(res_bookstore.text,'html.parser')
list_books = bs_bookstore.find_all(class_='product_pod')
for tag_books in list_books:
list_star = tag_books.find('p',class_='star-rating')
# 这个p标签的class属性有两种:'star-rating',以及具体的几星比如'Two'。我们选择所有书都有的class属性:'star-rating'
print('star-rating:',list_star['class'][1])
ok?ok就继续往下,不ok就多看多操作几遍
价格提取
价格这个书籍的信息是比较简单的,同样可以借助小箭头的工具,帮助我们快速地定位找这个标签
结果出来了,是<p class='price_color'>这个标签,而价格的信息是该标签的文本内容
思路出来了不?定位到该标签,再提取出文本内容
怎么提取文本内容?用Tag.text的格式
选择语言
import requests
from bs4 import BeautifulSoup
res_bookstore = requests.get('http://books.toscrape.com/catalogue/category/books/travel_2/index.html')
bs_bookstore = BeautifulSoup(res_bookstore.text,'html.parser')
list_books = bs_bookstore.find_all(class_='product_pod')
for tag_books in list_books:
tag_price = tag_books.find('p',class_='price_color') # 价格比较好找,根据属性提取,或者标签与属性一起都可以
print('Price:',tag_price.text, end='\n'+'------'+'\n') # 打印的时候,我加上了换行,为了让数据更加清晰地分隔开,当然你也可以不加。
这样,我们就分别完成了书名、评分、价格三个书籍信息的提取了
当然,最后我们还需要把这三部分的内容整合一下,完整的代码,自己尝试写写看吧!
练习-博客文章-参考
题目要求:你需要爬取的是博客(https://wordpress-edu-3autumn.localprod.forc.work/),首页的四篇文章信息,并且打印提取到的信息。
提取每篇文章的:
- 文章标题
- 发布时间
- 文章链接
网页URL:https://spidermen.cn/
【提示】
文章题目与URL的提取方式稍有不同:
``` html
<h2 class='entry-title'>
<a href='https://wordpress-edu-3autumn.localprod.forc.work/all-about-the-future_04/' rel='bookmark'>未来已来(四)——Python学习进阶图谱</a>
</h2>
```
文章标题可以通过`<h2>`与`<class_='entry-title'>`找到,并用`Tag.text`提取出。
而文章URL必须定位到`<a>`元素才可以。
【解答】
选择语言
import requests
from bs4 import BeautifulSoup
url_destnation = 'https://spidermen.cn/'
res_destnation = requests.get (url_destnation)
print(res_destnation.status_code) # 打印响应码
bs_articles = BeautifulSoup(res_destnation.text,'html.parser')
list_articles = bs_articles.find_all('header', class_ = 'entry-header') # 首先找到每篇文章所在的相同的元素
for tag_article in list_articles: # 遍历列表
tag_title = tag_article.find('h2',class_ = 'entry-title') # 找文章标题
tag_url = tag_article.find('a',rel = 'bookmark') # 找文章链接
tag_date = tag_article.find('time',class_='entry-date published') # 找文章发布时间
print(tag_title.text,'发布于:',tag_date.text) # 打印文章标题与发布时间
print(tag_url['href']) # 换行打印文章链接,需要使用属性名提取属性值
第3关
练习-豆瓣爬虫-参考
第一步:分析问题,明确结果
问题需求就是把豆瓣TOP250里面的 序号/电影名/评分/推荐语/链接 都爬取下来,结果就是全部展示打印出来
【讲解】
问题需求就是把豆瓣TOP250里面的 序号/电影名/评分/推荐语/链接 都爬取下来,结果就是全部展示打印出来
第二步:思考要用到的知识
要爬取“序号/电影名/评分/推荐语/链接”这些信息,我们已经学习了用requests.get()获取数据,BeautifulSoup库解析数据,find()和find_all()提取数据,还有呢,观察下,一共10页,我们还要加个for循环对吧~
https://shimo.im/docs/QWQJYGw8CtcwQwyq/ 《豆瓣250爬虫思路详解》
豆瓣电影爬取思路1
首先要提醒同学,因为我们课堂上练习的同学太多啦,所以现在豆瓣会对我们的ip进行反爬的限制。通常报错为NoneType Error,这种情况下,同学可以在本地编辑器运行没问题就ok,课程的空代码运行跳过哦
思路1的方法是:先爬取最小共同父级标签 `<li>`,然后针对每一个父级标签,提取里面的序号/电影名/评分/推荐语/链接。
先看看代码吧:
选择语言
import requests, random, bs4
for x in range(10):
headers = {
'user-agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'
# 标记了请求从什么设备,什么浏览器上发出
}
url = 'https://movie.douban.com/top250?start=' + str(x*25) + '&filter='
res = requests.get(url,headers=headers)
bs = bs4.BeautifulSoup(res.text, 'html.parser')
bs = bs.find('ol', class_='grid_view')
for titles in bs.find_all('li'):
num = titles.find('em',class_='').text
#查找序号
title = titles.find('span', class_='title').text
#查找电影名
tes = titles.find('span',class_='inq').text
#查找推荐语
comment = titles.find('span',class_='rating_num').text
#查找评分
url_movie = titles.find('a')['href']
print(num + '.' + title + '——' + comment + '\n' + '推荐语:' + tes +'\n' + url_movie)
看着我们需要的信息一条条蹦出来,还是很兴奋的哈哈哈
到144条怎么停下来报错了,哎?不要慌
那我们看看报错信息是啥呢:AttributeError: 'NoneType' object has no attribute 'text'
意思是:爬取到NoneType的数据不能直接使用.text
为什么会有NoneType呢?是在执行这一句tes = titles.find('span',class_='inq').text的时候报错了
再看到上面,是爬取到144这个电影时候停下来的,那我们找到这个电影吧!
这就是问题所在,144这部电影没有推荐语,而空值是没办法做text文本转换的,因此报错了呢
那怎么解决呢,聪明的你一定想到啦,加一个判断就可以啦!
判断电影是否有推荐语,有的话则打印,没有则不打印
修改过代码,我们再试一下哦
选择语言
import requests, random, bs4
for x in range(10):
headers = {
'user-agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'
# 标记了请求从什么设备,什么浏览器上发出
}
url = 'https://movie.douban.com/top250?start=' + str(x*25) + '&filter='
res = requests.get(url,headers=headers)
bs = bs4.BeautifulSoup(res.text, 'html.parser')
bs = bs.find('ol', class_='grid_view')
for titles in bs.find_all('li'):
num = titles.find('em',class_='').text
title = titles.find('span', class_='title').text
comment = titles.find('span',class_='rating_num').text
url_movie = titles.find('a')['href']
if titles.find('span',class_='inq') != None:
tes = titles.find('span',class_='inq').text
print(num + '.' + title + '——' + comment + '\n' + '推荐语:' + tes +'\n' + url_movie)
else:
print(num + '.' + title + '——' + comment + '\n' +'\n' + url_movie)
好啦,250个电影的信息都抓取到了耶!
到这里其实我们已经完成了抓取,但是再思考一下,除了加if判断之外,还有没有学过相关的debug知识可以使用起来呢?
在基础语法我们曾学道try...except...的方法,不知道大家还有没有印象呢?
我们可以试一下,正常情况下,用try打印出所有电影的信息;而当遇到报错的时候,执行到except,不打印电影推荐语这一项信息
再来看看代码吧:
选择语言
import requests, random, bs4
for x in range(10):
headers = {
'user-agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'
# 标记了请求从什么设备,什么浏览器上发出
}
url = 'https://movie.douban.com/top250?start=' + str(x*25) + '&filter='
res = requests.get(url,headers=headers)
bs = bs4.BeautifulSoup(res.text, 'html.parser')
bs = bs.find('ol', class_='grid_view')
for titles in bs.find_all('li'):
try:
num = titles.find('em',class_='').text
#查找序号
title = titles.find('span', class_='title').text
#查找电影名
tes = titles.find('span',class_='inq').text
#查找推荐语
comment = titles.find('span',class_='rating_num').text
#查找评分
url_movie = titles.find('a')['href']
print(num + '.' + title + '——' + comment + '\n' + '推荐语:' + tes +'\n' + url_movie)
except:
print(num + '.' + title + '——' + comment + '\n' + '推荐语:无' +'\n' + url_movie)
以上就是两种处理报错的方式,同学们多消化一下哦!
豆瓣电影爬取思路2
首先要提醒同学,因为我们课堂上练习的同学太多啦,所以现在豆瓣会对我们的ip进行反爬的限制。通常报错为NoneType Error,这种情况下,同学可以在本地编辑器运行没问题就ok,课程的空代码运行跳过哦
思路2的方法是:分别提取所有的序号/所有的电影名/所有的评分/所有的推荐语/所有的链接,然后再按顺序一一对应起来。
一个个找吧,有点多,有点复杂,但是不怕它!每每去研究一个网站都是要花不少时间的,但学习不怕花时间哦。来吧!
1、电影序号的标签定位
2、电影名的标签定位
3、电影链接的标签定位
我们可以借助小箭头工具,快速定位到相关的标签,并通过展开右边的各个标签后,大概找到我们需要的内容。
到这一步,同学可能会着手尝试,把每个数据对于的标签都单独的find_all()一遍
尝试一下是可以的,但是这里咱们仔细观察一下,上面的三种数据,是不是都包含在<div class='item'>这个标签下呢?
对吧?那我们可以三合一,先直接定位到<div class='item'>,后面再去进行这个标签下面的数据提取。
继续往下吧,还需要提取评分和推荐语
4、电影评分的标签定位
5、电影推荐语的标签定位
好,现在我们大概找到了所有相关信息的标签了
那我们再来理一理思路:
我们要爬取10页的内容,每一页用find_all()的方法去爬取标签,然后再通过遍历的方法,按顺序把需要的内容从find_all()得到的列表里提取出来做处理,再分别写入一个大的列表...
差不多就是这样,那来写写代码吧
选择语言
import requests
# 引用requests模块
from bs4 import BeautifulSoup
for x in range(10):
headers = {
'user-agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'
# 标记了请求从什么设备,什么浏览器上发出
}
url = 'https://movie.douban.com/top250?start=' + str(x*25) + '&filter='
res = requests.get(url,headers=headers)
bs = BeautifulSoup(res.text, 'html.parser')
tag_num = bs.find_all('div', class_='item')
# 查找包含序号,电影名,链接的<div>标签
tag_comment = bs.find_all('div', class_='star')
# 查找包含评分的<div>标签
tag_word = bs.find_all('span', class_='inq')
# 查找推荐语
list_all = []
for x in range(len(tag_num)):
list_movie = [tag_num[x].text[2:5], tag_num[x].find('img')['alt'], tag_comment[x].text[2:5], tag_word[x].text, tag_num[x].find('a')['href']]
list_all.append(list_movie)
print(list_all)
运行了一下,阿喔,报错了?
是不是第一反应就是想来找助教说:“助教你这个错啦!”
是的是的,上面这个报错了,那我们看看报错信息是啥呢:IndexError: list index out of range ,意思是:列表的索引超出了范围
那上面有这么多个索引的内容,我们需要去排查到底是哪一个索引出了问题
有两种方式:
方式1,把每个索引单独打印一遍,逐一排查;
方式2,看看已经打印的信息,是打印到哪个阶段才停下来的?然后根据已打印的情况去定位问题;
比如,助教在写这个文档的时候,运行这个程序,是在第176个电影的时候停下来的,如下图
那我们到网页上去找找这个电影吧,看看是出了什么小bug
哦豁,第143个电影信息没问题,但是第144个电影信息却是没有“推荐语”这一项的
这就是问题所在,没有推荐语,但是没有写入列表,所以会导致在索引的时候发生偏移量溢出的情况,所以报错了
行了,出了问题,我们就来想办法解决
既然有一部电影是没有推荐语的,那还会不会有其他电影也是没有推荐语的呢?
可能有,也可能没有。因为这个网页的信息是会在变化的,助教在写这个文档的时候,是找到了一共3个电影没有推荐语,除了上面的《绿皮书》,还有下面两部,序号分别是“144”、“208”、“240”、'’241'’、'’246’'
同学们在自己练习的时候,要以实际情况为准
助教这里直接把处理的思路告诉同学,我们可以用if条件加判断,当电影序号满足上面几个的情况时,补充一个“推荐语”给这部电影,这样就可以避免列表索引溢出的情况了
助教把代码都整理出来了
我们先来直接看一下代码,然后逐行去理解
选择语言
import requests
# 引用requests模块
from bs4 import BeautifulSoup
for x in range(10):
headers = {
'user-agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'
# 标记了请求从什么设备,什么浏览器上发出
}
url = 'https://movie.douban.com/top250?start=' + str(x*25) + '&filter='
res = requests.get(url,headers=headers)
bs = BeautifulSoup(res.text, 'html.parser')
tag_num = bs.find_all('div', class_='item')
# 查找包含序号,电影名,链接的<div>标签
tag_comment = bs.find_all('div', class_='star')
# 查找包含评分的<div>标签
tag_word = bs.find_all('span', class_='inq')
# 查找推荐语
list_all = []
for x in range(len(tag_num)):
if tag_num[x].text[2:5] == '144' or tag_num[x].text[2:5] =='208' or tag_num[x].text[2:5] =='240' or tag_num[x].text[2:5] =='241' or tag_num[x].text[2:5] =='246':
tag_word.insert(x,'没有推荐语')
list_movie = [tag_num[x].text[2:5], tag_num[x].find('img')['alt'], tag_comment[x].text[2:5], tag_word[x], tag_num[x].find('a')['href']]
else:
list_movie = [tag_num[x].text[2:5], tag_num[x].find('img')['alt'], tag_comment[x].text[2:5], tag_word[x].text, tag_num[x].find('a')['href']]
list_all.append(list_movie)
print(list_all)
最后,我当然知道你们还想问什么了
tag_word[x].text[2:5],这个[2:5]怎么来的?
我们先把[2:5]去掉打印看看咯?结果如下(举个列子所以我随意挑了126这部电影)
打印出来的文字信息很多,但是我们只需要126这三个数字
那126前面有两行空格,共占两个字符长度,偏移量为 [0] 和 [1]
而126三个数字,各占一个字符,偏移量分别为[2],[3],[4]
后面无关字符就是[5:]
根据左取右不取原则,我们只要把 [2:5] 这部分保留下来就ok啦
这个练习,重在提供一种解决问题的思路给同学,在实际运用中,并不是一个最好的爬取数据的方法。所以只要我们掌握了这种思路就可以了哦!
练习-一键下电影-参考
第一步:分析问题,明确目标
我们想要实现这样的功能:用户输入喜欢的电影名字,程序即可在电影天堂(https://www.ygdy8.com)爬取电影所对应的下载链接,并将下载链接打印出来。
【讲解】
我们知道爬虫是模拟人在浏览器的动作批量获取有价值的信息,那对于这道题
,我们先手动操作下,看看人是如何实现这个过程的。
首先,打开电影天堂(https://www.ygdy8.com) , 在”搜索“处,填写一部电影名,以”无名之辈“为例,然后,我们进入了“搜索结果”页面,最后,在下载页面滑到最下方,找到了下载地址。
人工操作的步骤,我们是不是可以将其分为 “输名字 - 查搜索结果 - 进入下载页面 - 找到下载链接” ? 呐,我们就让我们的爬虫也这样子走就可以了对吧~
第二步:分析讲解
“输名字 - 查搜索结果 - 进入下载页面 - 找到下载链接”
我们是这样找到下载链接的,那只要让我们的爬虫也走这样的步骤, 就可以达成我们的目标啦
【讲解】
步骤一
“输名字”,学过基础课
的同学一定可以想到,用input()就可以啦。
步骤二
”搜索结果
页面“ 这里面涉及到一个坑,我们要一起填上。
输入不同的电影名,观察搜索结果页面的URL:
《无名之辈》的搜索结果URL:http://s.ygdy8.com/plus/s0.php?typeid=1&keyword=
%CE%DE%C3%FB%D6%AE%B1%B2
《神奇动物》的搜索结果URL:http://s.ygdy8.com/plus/s0.php?typeid=1&keyword=
%C9%F1%C6%E6%B6%AF%CE%EF
《狗十三》 的搜索结果URL:http://s.ygdy8.com/plus/s0.php?typeid=1&ke
yword=%B9%B7%CA%AE%C8%FD
观察URL,不难发现:http://s.ygdy8.com/plus/s0.php?typeid=1&keyword=() 这些都是一样的,只不过不同的电影名对应URL后面加了一些我们看不懂的字符
请阅读以下代码,注意注释哦:
选择语言
a= '无名之辈'
b= a.encode('gbk')
# 将汉字,用gbk格式编码,赋值给b
print(quote(b))
# quote()函数,可以帮我们把内容转为标准的url格式,作为网址的一部分打开
输出结果是:
%
CE%DE%C3%FB%D6%AE%B1%B2
记得在本地IDE上试着敲下哦,可以把'无名之辈’换成神奇
动物和狗十三,看看输出结果是否和对应的编码一致。
诶,发现的确是一致的,那我们一起来解读这段代码是怎么实现的呢。
注释中提到了gbk格式编码,那gbk是什么呢?
_GBK编码_ <br/>
GBK是中国标准,只在中国使用,并没有表示大多数其它国家的编码;
而各国又陆续推出各自的编码标准,互不兼容,非常不利于全球化发展。
于是后来国际组织发行了一个全球统一编码表,把全球各国文字都统一在一个编码标准里,名为Unicode。
由此我们知道,想把中文转换成url格式,需要先用encode('gbk')将其转成gbk编码,然
后再用quote()把它转化成url的一部分。
然后再将它与[http://s.ygdy8.com/plus/so.php?typeid=1&keywor
d=]()拼接起来就是电影的搜索结果页面啦~
好,总结下上面的这个知识点,如何建立“输入电影名”与“搜索结果页面”的联系:
__中文 - gbk - url - 拼接__
这样我们就把完整的搜索结果页面找到并提取出来了。<br>
步骤三 + 步骤四
”进入下载页面“ 与 “找到下载链接” 就是解析网页定位啦,利用find() 和 find_all(),都是你会的内容,加油呀~
第三步:书写代码吧 (。▰‿‿▰。) ❤
前两步我们已经捋顺了思路,还填了一个小坑,现在要自己敲代码咯,对转换格式编码的代码记不清的同学,可以偷偷看下提示哦。
【提示】
将汉字,用gbk格式编码,赋值,这时需要encode(),还需要用到quote()函数,可以帮我们把内
容转为标准的url格式,作为网址的一部分打开。
定位下载链接还是要用到find()哦~
有些电影没有下载链接,记得加个判断呀~
【解答】
选择语言
import requests
from bs4 import BeautifulSoup
from urllib.request import quote
#quote()函数,可以帮我们把内容转为标准的url格式,作为网址的一部分打开
movie = input('你想看什么电影呀?')
gbkmovie = movie.encode('gbk')
#将汉字,用gbk格式编码,赋值给gbkmovie
url = 'http://s.ygdy8.com/plus/s0.php?typeid=1&keyword='+quote(gbkmovie)
#将gbk格式的内容,转为url,然后和前半部分的网址拼接起来。
res = requests.get(url)
#下载××电影的搜索页面
res.encoding ='gbk'
#定义res的编码类型为gbk
soup_movie = BeautifulSoup(res.text,'html.parser')
#解析网页
urlpart = soup_movie.find(class_='co_content8').find_all('table')
if urlpart:
urlpart = urlpart[0].find('a')['href']
urlmovie = 'https://www.ygdy8.com/' + urlpart
res1 = requests.get(urlmovie)
res1.encoding = 'gbk'
soup_movie1 = BeautifulSoup(res1.text,'html.parser')
urldownload = soup_movie1.find('div',id='Zoom').find('span').find('table').find('a')['href']
print(urldownload)
else:
print('没有' + movie)
# 有些电影是查询不到没下载链接的,因此加了个判断
第5关
练习-歌词爬取-参考
第一步:分析问题,明确结果
问题需求就是把关卡内的代码稍作修改,将周杰伦前五页歌曲的歌词都爬取下来,结果就是全部展示打印出来。
【讲解】
问题需求就是把关卡内的代码稍作修改,将周杰伦前五页歌曲的歌词都爬取下来,结果就是全部展示打印出来。
第二步:写代码
【提示】
Network - XHR-client_search - Headers - Query String Parameters , 观察里面参数的变化
【解答】
https://y.qq.com/portal/search.html#page=1&searchid=1&remoteplace=txt.yqq.top&t=lyric&w=%E5%91%A8%E6%9D%B0%E4%BC%A6
爬取歌词这道题,有的同学找歌词的时候,会直接点进一首歌的详情页,然后打开“检查”,先在element去找,然后按照<div class='lyric_cont_box'>去定位
结果发现是爬取不到内容的
那爬不到的时候,我们就要去看看Network里面的内容
检查一下,ALL的第一个请求,是否网页的第0个请求里面是否有我们要抓取的内容?
操作如下,选择Network,选择ALL,刷新一下页面,选择第一个请求,名称是“004Fs2FP1EvZYc.html”
看看Preveiw的内容,好像没有我们想要的歌词内容也
那咋办?走!到XHR找找去
寻寻觅觅之后,发现了有这么一个请求,名称是:fcg_query_lyric_yqq.fcg......反正有个lyric(歌词的意思)
点开看看Preview的内容,果然有歌词
接下来,爬它!
既然是在XHR里面的内容,那爬取的方法就是:
用requests.get()去请求数据,请求数据后,需要用json()进行解析,解析完成后再按字典和列表的格式去提取数据
选择语言
import re,requests
lyric_url = 'https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_yqq.fcg'
headers = {
'origin': 'https://y.qq.com',
'referer': 'https://y.qq.com/n/yqq/song/004Fs2FP1EvZYc.html',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36'
}
params = {
'nobase64': '1',
'musicid': '244115385',
'-': 'jsonp1',
'g_tk': '',
'loginUin': '0',
'hostUin': '0',
'format': 'json',
'inCharset': 'utf8',
'outCharset': 'utf-8',
'notice': '0',
'platform': 'yqq.json',
'needNewCode': '0'
}
res_lyric = requests.get(lyric_url,headers=headers,params=params)
json_lyric = res_lyric.json()
lyric = json_lyric['lyric']
print(lyric)
打印出来的结果如下:
爬是爬下来了,这也太乱七八糟了吧。助教有一种方法,把数据处理一下
在上面代码的最末尾,加上这么一段即可:
选择语言
for i in lyric.split('
'):
music_str = re.sub('[A-Za-z0-9\\!\\%\\[\\]\\,\\。\\&\\#\\;]', '', i)#用正则表达式去掉无用的符号字符
if music_str.strip():#strip去掉空行
print(music_str)
简单地说一下这里用到的是“正则表达式”的知识,因为课堂里没有这个知识点的讲解,所以助教这里不做拓展哈~
到这里,我们确实爬取到了歌词的内容,但是,这样却只能爬取到一首歌的歌词啊。好像不符合题目要求也,那...我们是不是找错了?
嗯,也不是错,但是不太符合题目的要求
其实,题目原本希望我们去抓取的,是下面这个页面
在歌手的搜索页里,我们可以切换到“歌词”这一块的内容
同样我们先在ALL里面查看第0个请求
然后我们发现,数据依然不在这里。在这个html里面,是没有我们需要抓取的歌词内容信息的
也就是说,我们没有办法通过element去爬取歌词
下一步应该怎么做?
....(思考一下)
或许你已经想到了,我们就得从XHR里面去找了
如果没找到,那我们刷新一下页面,再用助教之前讲过的小技巧,给请求按Size排序来快速找到我们需要的请求。果然,client_search_cp 这个请求是最大的,排在最前面
既然是在XHR里面的内容,那再回忆一下爬取的方法:
1、requests.get()去请求数据,记得带上参数Params和Headers
2、请求数据后,需要用json()进行解析
3、解析完成后再按字典和列表的格式去提取数据
选择语言
import requests
import json
# 引用requests,json模块
url = 'https://c.y.qq.com/soso/fcgi-bin/client_search_cp'
headers = {
'referer':'https://y.qq.com/portal/search.html',
# 请求来源
'user-agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'
# 标记了请求从什么设备,什么浏览器上发出
}
for x in range(5):
params = {
'ct':'24',
'qqmusic_ver': '1298',
'new_json':'1',
'remoteplace':'sizer.yqq.lyric_next',
'searchid':'94267071827046963',
'aggr':'1',
'cr':'1',
'catZhida':'1',
'lossless':'0',
'sem':'1',
't':'7',
'p':str(x+1),
'n':'10',
'w':'周杰伦',
'g_tk':'1714057807',
'loginUin':'0',
'hostUin':'0',
'format':'json',
'inCharset':'utf8',
'outCharset':'utf-8',
'notice':'0',
'platform':'yqq.json',
'needNewCode':'0'
}
res = requests.get(url, params = params)
#下载该网页,赋值给res
jsonres = res.json()
#使用json来解析res.text
list_lyric = jsonres['data']['lyric']['list']
#一层一层地取字典,获取歌词的列表
for lyric in list_lyric:
#lyric是一个列表,x是它里面的元素
print(lyric['content'])
#以content为键,查找歌词
果然,这样我们可以实现5个页面所有歌词的爬取了!
这个过程,也就是我们经过一番研究之后发现,从最初搜索结果的页面去爬取歌词是最方便简洁的
再补充一下,爬取歌词这个练习,第一次爬取下来的歌词呈现的效果如下
爬是爬下来了,怎么又太乱七八糟了吧。助教还有一种方法,把数据处理一下
解决方法:歌词里面\n其实是\\n来的,用字符串.replace('\\n','\n')就可以换行了,这是因为爬出来的歌词里面的\\n,然后打印的时候遇到\\n,会打印成\n
参考一下代码:
选择语言
for lyric in list_lyric:
print(lyric['content'].replace('\\n','\n'))
隐藏的终极版本:(电脑打开下载可看)
5关_歌词单曲页爬取.py2.9KB
练习-头条粉丝-参考
第一步: 阅读代码
作为头号粉丝,只爬取歌词不能满足迷妹迷弟们~我们还想要爬取自己喜欢的歌手音乐信息~
现在,请你阅读关卡内代码,思考应该如何修改它。
【提示】
阅读代码就可以了。
【讲解】
选择语言
import requests, json
#引用requests,json模块
url = 'https://c.y.qq.com/soso/fcgi-bin/client_search_cp'
for x in range(5):
params = {
'ct':'24',
'qqmusic_ver': '1298',
'new_json':'1',
'remoteplace':'txt.yqq.song',
'searchid':'70717568573156220',
't':'0',
'aggr':'1',
'cr':'1',
'catZhida':'1',
'lossless':'0',
'flag_qc':'0',
'p':str(x+1),
'n':'20',
'w':'周杰伦',
'g_tk':'714057807',
'loginUin':'0',
'hostUin':'0',
'format':'json',
'inCharset':'utf8',
'outCharset':'utf-8',
'notice':'0',
'platform':'yqq.json',
'needNewCode':'0'
}
# 将参数封装为字典
res_music = requests.get(url,params=params)
# 调用get方法,下载这个列表
json_music = res_music.json()
# 使用json()方法,将response对象,转为列表/字典
list_music = json_music['data']['song']['list']
# 一层一层地取字典,获取歌单列表
for music in list_music:
# list_music是一个列表,music是它里面的元素
print(music['name'])
# 以name为键,查找歌曲名
print('所属专辑:'+music['album']['name'])
# 查找专辑名
print('播放时长:'+str(music['interval'])+'秒')
# 查找播放时长
print('播放链接:https://y.qq.com/n/yqq/song/'+music['mid']+'.html\n\n')
# 查找播放链接
运行右侧代码发现我们只爬取到了周杰伦的歌曲信息,那么想做到一键换成任何喜欢的歌手该怎么做呢? 我们发现params里面的`w`对应的是歌手名字,那么我们就可以用input来替换掉呀~
第二步: 修改代码
作为头号粉丝,只爬取歌词不能满足迷妹迷弟们~我们还想要爬取自
己喜欢的歌手音乐信息~
现在,请你尝试修改代码。借助input()函数,实现爬取任意一个歌手的音乐信息。
【提示】
singer = input('你最喜欢的歌手是谁呀?')
【解答】
选择语言
import requests, json
# 引用requests模块
url = 'https://c.y.qq.com/soso/fcgi-bin/client_search_cp'
singer = input('你喜欢的歌手是谁呢?')
for x in range(6):
params = {
'ct':'24',
'qqmusic_ver': '1298',
'new_json':'1',
'remoteplace':'txt.yqq.song',
'searchid':'70717568573156220',
't':'0',
'aggr':'1',
'cr':'1',
'catZhida':'1',
'lossless':'0',
'flag_qc':'0',
'p':str(x+1),
'n':'20',
'w':singer,
'g_tk':'714057807',
'loginUin':'0',
'hostUin':'0',
'format':'json',
'inCharset':'utf8',
'outCharset':'utf-8',
'notice':'0',
'platform':'yqq.json',
'needNewCode':'0'
}
# 将参数封装为字典
res_music = requests.get(url,params=params)
# 调用get方法,下载这个列表
json_music = res_music.json()
# 使用json()方法,将response对象,转为列表/字典
list_music = json_music['data']['song']['list']
# 一层一层地取字典,获取歌单列表
for music in list_music:
# list_music是一个列表,music是它里面的元素
print(music['name'])
# 以name为键,查找歌曲名
print('所属专辑:'+music['album']['name'])
# 查找专辑名
print('播放时长:'+str(music['interval'])+'秒')
# 查找播放时长
print('播放链接:https://y.qq.com/n/yqq/song/'+music['mid']+'.html\n\n')
# 查找播放链接
练习-一键查快递-参考
第一步:分析需求,明确目标
实现功能:用户输入快递名称和单号,程序即可在快递100(https://www.kuaidi100.com/)爬取最新物流状态,并将其打印出来。
【讲解】
先进入快递100(https://www.ku~如图,红框框里的是我们需要输入与想要提取的信息:
第二步:书写代码吧 (。▰‿‿▰。) ❤
如果没思路,可以偷偷看下提示哦~
【提示】
Network - query?type... - Headers - Query String Parameters , 观察里面参数的变化~
服务器返回的内容,是json的格式。我们可以用处理列表、处理字典的手段来提取物流状态哦
【解答】
现在我们会发现根本查找不到正确的快递信息,这是因为快递一百增加了反爬虫,temp是系统随机参数,可以自己随机生成的,但是这个练习现在出不了正确的物流信息,所以现在当练习使用哦~后面第8关会讲到cookies的内容,到时可以再使用这个来做这个练习哈~
下面就是加上了cookies和randon的参考代码(需要自己更新一下cookies才有效哦):
选择语言
import requests
import random
#调用requests模块,负责上传和下载数据
cookie = input('请输入网页的cookie值')
kuaidiType = input('请输入快递类型(拼音)')
kuaidiID = input('请输入快递单号')
url = 'https://www.kuaidi100.com/query?'
#使用get需要一个链接
header = {
'Accept-Encoding': 'gzip, deflate, br',
'referer': 'https://www.kuaidi100.com/',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
'Connection': 'keep-alive',
'Cookie': cookie,
'Host': 'www.kuaidi100.com',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36',
'X-Requested-With': 'XMLHttpRequest'
}
a = random.random()
params = {
'type': kuaidiType,
'postid': kuaidiID,
'temp':str(a),
'phone':''
}
#将需要get的内容,以字典的形式记录在params内
r = requests.get(url, params=params,headers = header)
result = r.json()
for i in result['data']:
print(i['context'])
#记得观察preview里面的参数哦