第0关 初识爬虫(涉及到得函数requests)
爬虫得4个步骤:
0 获取数据
1 解析数据
2 提取数据
3 储存数据
Request 库
import requests
res = requests.get("url")
Request常用得四个属性
① requests.status_code 检查请求是否成功
② requests.content 把requests转换为二进制数据
③ requests.text 把requests对象转换为二进制数据
④ requests.encoding 定义requests对象的编码(200表示请求成功)
import requests
#引用requests库
res = requests.get('https://localprod.pandateacher.com/python-manuscript/crawler-html/sanguo.md')
#下载《三国演义》第一回,我们得到一个对象,它被命名为res
novel=res.text
#把Response对象的内容以字符串的形式返回
print(novel[:800])
#现在,可以打印小说了,但考虑到整章太长,只输出800字看看就好。在关于列表的知识那里,你学过[:800]的用法。
import requests
res = requests.get("url")
res.encoding = "gbk"
print(res.status_code)
print(res.content)
练习:
import requests
res = requests.get("https://res.pandateacher.com/2019-01-12-15-29-33.png")
p = res.content
print(p)
第一关 我也可以写一个网页(含有HTML的常见标签)
HTML文档:
①层级结构:
主要由元素组成
缩进代表html的层级
<html>
<head></head>
<body></body>
</html>
②标签
空标签:(<>或</>)
闭标签:(<>和</>)
③属性:
标签内的赋值语句:(width = “20px”)
class 可定义相同类型的元素
id 是定义元素的唯一标识
④元素
开始标签和结束标签内的所以代码
**标签:**闭合标签和空标签
闭合标签,它们绝大多数成对出现(有开始标签<>,也有结束标签</>),如:
空标签,顾名思义,指那些“孤苦伶仃”的单标签,它们“形影单只”只有一个尖括号<>(斜杠/可省略),标签开始即结束,比如上面的是图片标签,是链接标签,是input标签。
值得注意的是,不要把标签与我们前文讲的元素混淆了哦,前面我们说的元素,其实是包含了开始标签与结束标签内的所有代码。如html元素是指包括标签内的所有代码。
第二关:爬虫初体验(涉及到的函数BeautifulSoup)
bs对象 = BeautifulSoup(要解析的文本,“解析器”)
在括号中,要输入两个参数,第1个参数是要被解析的文本,注意了,它必须必须必须是字符串。
import requests
res = resquests.get("url")
soup = BeatifulSoup(res.text,"html.parser")
find()与find_all()
find():find()只提取首个满足要求的数据。find()方法将代码从上往下找,找到符合条件的第一个数据,不管后面还有没有满足条件的其他数据,停止寻找,立即返回。
find_all():而find_all()顾名思义(find all:查找全部),提取出的是所有满足要求的数据。代码从上往下找,一直到代码的最后,把所有符合条件的数据揣好,一起打包返回。
使用方法:
方法 作用 用法 示例
find() 提取满足要求的首个数据 BeautifulSoup对象.find(标签,属性) soup.find(“div”,class_=“books”)
find_all() 提取满足要求的所有数据 BeautifulSoup对象.find_all(标签,属性) soup.find_all(“div”,class_=“books”)
**注意:**find_all()返回的值为列表,可以利用列表的公式去调用他
首先,请看举例中括号里的class_,这里有一个下划线,是为了和python语法中的类 class区分,避免程序冲突。当然,除了用class属性去匹配,还可以使用其它属性,比如style属性等。
其次,括号中的参数:标签和属性可以任选其一,也可以两个一起使用,这取决于我们要在网页中提取的内容。
Tag对象的三种常用属性与方法:
属性/方法 作用
Tag.find()和Tag.find_all() 提取Tag中的Tag
Tag.text 提取Tag中的文字
Tag[“属性名”] 输入参数:属性名,可以提取Tag中这个属性的值
# 调用requests库
import requests
# 调用BeautifulSoup库
from bs4 import BeautifulSoup
# 返回一个response对象,赋值给res
res =requests.get('https://localprod.pandateacher.com/python-manuscript/crawler-html/spider-men5.0.html')
# 把res解析为字符串
html=res.text
# 把网页解析为BeautifulSoup对象
soup = BeautifulSoup( html,'html.parser')
# 通过匹配属性class='books'提取出我们想要的元素
items = soup.find_all(class_='books')
# 遍历列表items
for item in items:
# 在列表中的每个元素里,匹配标签<h2>提取出数据
kind = item.find('h2')
# 在列表中的每个元素里,匹配属性class_='title'提取出数据
title = item.find(class_='title')
# 在列表中的每个元素里,匹配属性class_='info'提取出数据
brief = item.find(class_='info')
# 打印书籍的类型、名字、链接和简介的文字
print(kind.text,'\n',title.text,'\n',title['href'],'\n',brief.text)
find()和find_all中的全部属性:
find(tag,attributes,recursive,text,keywords)
find_all(tag,attributes,recursive,text,limit,keywords)
练习:
爬取书店内容
import requests
from bs4 import BeautifulSoup
res = requests.get("http://books.toscrape.com/")
html = res.text
soup = BeautifulSoup(html,"html.parser")
out = soup.find("ul",class_="nav nav-list").find_all("li")
for a in out:
tag_name = a.find("a")
print(tag_name.text.strip())
import requests
from bs4 import BeautifulSoup
res_bookstore = requests.get("http://books.toscrape.com/catalogue/category/books/travel_2/index.html")
html = res_bookstore.text
bs_bookstore = BeautifulSoup(html,"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")
list_star = tag_books.find("p",class_="star-rating")
list_price = tag_books.find("p",class_="price_color")
print(tag_name["title"])
print("star_rating:",list_star["class"][1])
print("price:",list_price.text)
import requests
from bs4 import BeautifulSoup
res = requests.get("https://wordpress-edu-3autumn.localprod.oc.forchange.cn/")
html = res.text
bs1 = BeautifulSoup(html,"html.parser")
all = bs1.find_all("header",class_="entry-header")
for i in all:
title = i.find("h2",class_="entry-title")
time = i.find("a").find("time",class_="entry-date published")
data = i.find("a",rel="bookmark")
print("文章标题为",title.text)
print("文章发布时间为",time.text)
print("文章链接为",data["href"])
第三关:解密吴氏私厨
提取最小夫标签
# 引用requests库
import requests
# 引用BeautifulSoup库
from bs4 import BeautifulSoup
# 为躲避反爬机制,伪装成浏览器的请求头
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'}
# 获取数据
res_foods = requests.get('http://www.xiachufang.com/explore/',headers=headers)
# 解析数据
bs_foods = BeautifulSoup(res_foods.text,'html.parser')
# 查找最小父级标签
list_foods = bs_foods.find_all('div',class_='info pure-u')
# 打印最小父级标签
print(list_foods)
注意:有时会打印一些奇怪的东西当遇到这种糟糕的情况,一般有两种处理方案:数量太多而无规律,我们会换个标签提取;数量不多而有规律,我们会对提取的结果进行筛选——只要列表中的若干个元素就好。
# 引用requests库
import requests
# 引用BeautifulSoup库
from bs4 import BeautifulSoup
# 为躲避反爬机制,伪装成浏览器的请求头
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'}
# 获取数据
res_foods = requests.get('http://www.xiachufang.com/explore/',headers=headers)
# 解析数据
bs_foods = BeautifulSoup(res_foods.text,'html.parser')
# 查找最小父级标签
list_foods = bs_foods.find_all('div',class_='info pure-u')
# 提取第0个父级标签中的<a>标签
tag_a = list_foods[0].find('a')
# 输出菜名,使用strip()去掉了多余的空格
print(tag_a.text.strip())
# 输出URL
print('http://www.xiachufang.com'+tag_a['href'])
**纯文本:**当我们在用text获取纯文本时,获取的是该标签内的所有纯文本信息,不论是直接在这个标签内,还是在它的子标签内。
需要强调的一点是,text可以这样做,但如果是要提取属性的值,是不可以的。父标签只能提取它自身的属性值,不能提取子标签的属性值
from bs4 import BeautifulSoup
bs = BeautifulSoup('<p><a>惟有痴情难学佛</a>独无媚骨不如人</p>','html.parser')
tag = bs.find('p')
print(type(tag))
print(tag.text)
# 引用requests库
import requests
# 引用BeautifulSoup库
from bs4 import BeautifulSoup
# 为躲避反爬机制,伪装成浏览器的请求头
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'}
# 获取数据
res_foods = requests.get('http://www.xiachufang.com/explore/',headers=headers)
# 解析数据
bs_foods = BeautifulSoup(res_foods.text,'html.parser')
# 查找最小父级标签
list_foods = bs_foods.find_all('div',class_='info pure-u')
```python
# 引用requests库
import requests
# 引用BeautifulSoup库
from bs4 import BeautifulSoup
# 为躲避反爬机制,伪装成浏览器的请求头
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'}
# 获取数据
res_foods = requests.get('http://www.xiachufang.com/explore/',headers=headers)
# 解析数据
bs_foods = BeautifulSoup(res_foods.text,'html.parser')
# 查找最小父级标签
list_foods = bs_foods.find_all('div',class_='info pure-u')
# 创建一个空列表,用于存储信息
list_all = []
for food in list_foods:
tag_a = food.find('a')
# 菜名,使用strip()函数去掉多余的空格
name = tag_a.text.strip()
# 获取URL
URL = 'http://www.xiachufang.com'+tag_a['href']
tag_p = food.find('p',class_='ing ellipsis')
# 食材,使用strip()函数去掉多余的空格
ingredients = tag_p.text.strip()
# 将菜名、URL、食材,封装为列表,添加进list_all
list_all.append([name,URL,ingredients])
# 打印
print(list_all)
**第一种方法**
#创建一个空列表,用于存储信息
list_all = []
for food in list_foods:
tag_a = food.find('a')
# 菜名,使用strip()函数去掉多余的空格
name = tag_a.text.strip()
# 获取URL
URL = 'http://www.xiachufang.com'+tag_a['href']
tag_p = food.find('p',class_='ing ellipsis')
# 食材,使用strip()函数去掉多余的空格
ingredients = tag_p.text.strip()
# 将菜名、URL、食材,封装为列表,添加进list_all
list_all.append([name,URL,ingredients])
#打印
print(list_all)
**第二种方法**
# 引用requests库
import requests
# 引用BeautifulSoup库
from bs4 import BeautifulSoup
# 为躲避反爬机制,伪装成浏览器的请求头
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'}
# 获取数据
res_foods = requests.get('http://www.xiachufang.com/explore/',headers=headers)
# 解析数据
bs_foods = BeautifulSoup(res_foods.text,'html.parser')
# 查找包含菜名和URL的<p>标签
tag_name = bs_foods.find_all('p',class_='name')
# 查找包含食材的<p>标签
tag_ingredients = bs_foods.find_all('p',class_='ing ellipsis')
# 创建一个空列表,用于存储信息
list_all = []
# 启动一个循环,次数等于菜名的数量
for x in range(len(tag_name)):
# 提取信息,封装为列表。
list_food = [tag_name[x].text.strip(),tag_name[x].find('a')['href'],tag_ingredients[x].text.strip()]
# 将信息添加进list_all
list_all.append(list_food)
# 打印
print(list_all)
两个不同的解法
# 引用requests库
import requests
# 引用BeautifulSoup库
from bs4 import BeautifulSoup
# 为躲避反爬机制,伪装成浏览器的请求头
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'}
# 获取数据
res_foods = requests.get('http://www.xiachufang.com/explore/',headers=headers)
# 解析数据
bs_foods = BeautifulSoup(res_foods.text,'html.parser')
# 查找包含菜名和URL的<p>标签
tag_name = bs_foods.find_all('p',class_='name')
# 查找包含食材的<p>标签
tag_ingredients = bs_foods.find_all('p',class_='ing ellipsis')
# 创建一个空列表,用于存储信息
list_all = []
# 启动一个循环,次数等于菜名的数量
for x in range(len(tag_name)):
# 提取信息,封装为列表。
list_food = [tag_name[x].text.strip(),tag_name[x].find('a')['href'],tag_ingredients[x].text.strip()]
# 将信息添加进list_all
list_all.append(list_food)
# 打印
print(list_all)
# 以下是另外一种解法
# 查找最小父级标签
list_foods = bs_foods.find_all('div',class_='info pure-u')
# 创建一个空列表,用于存储信息
list_all = []
for food in list_foods:
tag_a = food.find('a')
# 菜名,使用strip()函数去掉了多余的空格
name = tag_a.text.strip()
# 获取URL
URL = 'http://www.xiachufang.com'+tag_a['href']
tag_p = food.find('p',class_='ing ellipsis')
# 食材,使用strip()函数去掉了多余的空格
ingredients = tag_p.text.strip()
# 将菜名、URL、食材,封装为列表,添加进list_all
list_all.append([name,URL,ingredients])
# 打印
print(list_all)
练习:
import requests,bs4
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'}
for x in range(10): #一页有25个电影,需要遍历十页就遍历十次
url = "https://movie.douban.com/top250?start="+str(x*25)+"&filter="
res = requests.get(url,headers = headers) #x*25是因为一页有25个电影,需要遍历250次所以开头就遍历了十次
bs = bs4.BeautifulSoup(res.text,"html.parser")
num = bs.find_all("em")
title = bs.find_all(class_="hd")
comment = bs.find(id = "content").find_all("div",class_="bd")
tes = bs.find_all("span",class_="inq")
for titles in range(len(num)): #利用电影的排名进行遍历250次
comments = comment[titles].find("span",class_="inq")
print(num[titles].text+".",end="")
print(title[titles].find("a").find("span",class_="title").text+""+"评分:"+comment[titles].find(class_="rating_num").text)
if comments:
print("推荐语:"+comments.text+"\n"+title[titles].find("a")["href"]+"\n")
else :
print("推荐语:"+"暂无推荐语" + "\n" + title[titles].find("a")["href"]+"\n")
练习:搜索电影下载链接
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')
# print(urlpart)
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)
# 有些电影是查询不到没下载链接的,因此加了个判断
第四关 寻找周杰伦(Network,XHR,json)
Network:
在你刚才打开的QQ音乐页面,调用“检查”(ctrl+shift+i)工具,然后点击Network。
Network的功能是:记录在当前页面上发生的所有请求。现在看上去好像空空如也的样子,这是因为Network记录的是实时网络请求。现在网页都已经加载完成,所以不会有东西。我们点击一下刷新,浏览器会重新访问网络,这样就会有记录。
json是什么呢?粗暴地来解释,json是一种数据交换的语法。对我们来说,它只是一种规范数据传输的格式,形式有点像字典和列表的结合体。我们在XHR里查看到的列表/字典,严格来说其实它不是列表/字典,它是json。
而json和XHR之间的关系:XHR用于传输数据,它能传输很多种数据,json是被传输的一种数据格式。就是这样而已。
我们总是可以将json格式的数据,转换成正常的列表/字典,也可以将列表/字典,转换成json
Network能够记录浏览器的所有请求。我们最常用的是:ALL(查看全部)/XHR(仅查看XHR)/Doc(Document,第0个请求一般在这里),有时候也会看看:Img(仅查看图片)/Media(仅查看媒体文件)/Other(其他)。最后,JS和CSS,则是前端代码,负责发起请求和页面实现;Font是文字的字体;而理解WS和Manifest,需要网络编程的知识,倘若不是专门做这个,你不需要了解。
第五关:狂热的粉丝
在上面,我们能看到每个url都由两部分组成。前半部分大多形如:https://xx.xx.xxx/xxx/xxx
后半部分,多形如:xx=xx&xx=xxx&xxxxx=xx&…… 两部分使用?来连接。
其实在这里,#和?的功能是一样的,作用都是分隔,若把链接的#替换成?,访问的效果是一样的(注意:用?分隔的url不一定可以用#代替)。
请求头Request Headers
user-agent(中文:用户代理)会记录你电脑的信息和浏览器版本(如我的,就是windows10的64位操作系统,使用谷歌浏览器)。
origin(中文:源头)和referer(中文:引用来源)则记录了这个请求,最初的起源是来自哪个页面。它们的区别是referer会比origin携带的信息更多些。
如果我们想告知服务器,我们不是爬虫,而是一个正常的浏览器,就要去修改user-agent。倘若不修改,那么这里的默认值就会是Python,会被服务器认出来。
如何添加Request Headers
import requests
url = 'https://c.y.qq.com/soso/fcgi-bin/client_search_cp'
headers = {
'origin':'https://y.qq.com',
# 请求来源,本案例中其实是不需要加这个参数的,只是为了演示
'referer':'https://y.qq.com/n/yqq/song/004Z8Ihr0JIu5s.html',
# 请求来源,携带的信息比“origin”更丰富,本案例中其实是不需要加这个参数的,只是为了演示
'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36',
# 标记了请求从什么设备,什么浏览器上发出
}
# 伪装请求头
params = {
'ct':'24',
'qqmusic_ver': '1298',
'new_json':'1',
'remoteplace':'sizer.yqq.song_next',
'searchid':'59091538798969282',
't':'0',
'aggr':'1',
'cr':'1',
'catZhida':'1',
'lossless':'0',
'flag_qc':'0',
'p':'1',
'n':'20',
'w':'周杰伦',
'g_tk':'5381',
'loginUin':'0',
'hostUin':'0',
'format':'json',
'inCharset':'utf8',
'outCharset':'utf-8',
'notice':'0',
'platform':'yqq.json',
'needNewCode':'0'
}
# 将参数封装为字典
res_music = requests.get(url,headers=headers,params=params)
# 发起请求,填入请求头和参数
我们来让这个代码变好看些。事实上,requests模块里的requests.get()提供了一个参数叫params,可以让我们用字典的形式,把参数传进去。
import requests
# 引用requests模块
url = 'https://c.y.qq.com/base/fcgi-bin/fcg_global_comment_h5.fcg'
# 请求歌曲评论的url参数的前面部分
for i in range(5):
params = {
'g_tk':'5381',
'loginUin':'0',
'hostUin':'0',
'format':'json',
'inCharset':'utf8',
'outCharset':'GB2312',
'notice':'0',
'platform':'yqq.json',
'needNewCode':'0',
'cid':'205360772',
'reqtype':'2',
'biztype':'1',
'topid':'102065756',
'cmd':'6',
'needmusiccrit':'0',
'pagenum':str(i),
'pagesize':'15',
'lasthotcommentid':'song_102065756_3202544866_44059185',
'domain':'qq.com',
'ct':'24',
'cv':'10101010'
}
# 将参数封装为字典
res_comments = requests.get(url,params=params)
# 调用get方法,下载这个字典
json_comments = res_comments.json()
list_comments = json_comments['comment']['commentlist']
for comment in list_comments:
print(comment['rootcommentcontent'])
print('-----------------------------------')