我要偷偷的学Python,然后惊呆所有人(第九天)

我要偷偷的学Python,然后惊呆所有人(第九天)

标题无意冒犯,就是觉得这个广告挺好玩的
上面这张思维导图喜欢就拿走,反正我也学不了这么多

前言

前期回顾:我要偷偷学Python(第八天)

上一篇呢,上一篇我们了解了一下网页的基本结构,并且通过对网页的分析抓取了一点数据出来。
但是我们就这么满足了吗?这显然是不可能的,你见过哪个爬虫就爬几个字嘛。

所以今天,我们来一次性爬上一大波数据!!!
我行,你也行!!!

我要偷偷的学Python,然后惊呆所有人(第九天)
插播一条推送:(如果是小白的话,可以看一下下面这一段)

欢迎来到我们的圈子

我建了一个Python学习答疑群,有兴趣的朋友可以了解一下:这是个什么群

群里已经有四百多个小伙伴了哦!!!

直通群的传送门:传送门


本系列文默认各位有一定的C或C++基础,因为我是学了点C++的皮毛之后入手的Python,这里也要感谢齐锋学长送来的支持。
本系列文默认各位会百度,学习‘模块’这个模块的话,还是建议大家有自己的编辑器和编译器的,上一篇已经给大家做了推荐啦?

我要的不多,点个关注就好啦
然后呢,本系列的目录嘛,说实话我个人比较倾向于那两本 Primer Plus,所以就跟着它们的目录结构吧。

本系列也会着重培养各位的自主动手能力,毕竟我不可能把所有知识点都给你讲到,所以自己解决需求的能力就尤为重要,所以我在文中埋得坑请不要把它们看成坑,那是我留给你们的锻炼机会,请各显神通,自行解决。

哎,怪我孤陋寡闻,实在找不到适合我们这个阶段的网站,我的爬虫又不断地让人捏死,只好借鉴别人的栗子了。。。

开手练习:爬取网上书店

目标网址:http://books.toscrape.com/
任务:爬取目标网址中的分类目录:
我要偷偷的学Python,然后惊呆所有人(第九天)

干!

有没有思路?没思路看我讲。

第一步:找到最小公共父标签

这个会找吧:
我要偷偷的学Python,然后惊呆所有人(第九天)

第一步,点亮匹配按钮(以后我就叫它匹配按钮了)
第二步,把鼠标放到要选的区域,注意,要颜色完全覆盖住你要选的区域,覆盖不住调整鼠标位置。
第三步,左击鼠标,定位代码。
第四步,再看一眼那行标签是不是最小且公共的了,有虚线,可以看到那行标签管到哪一层。

其实你再认真找一下,就会发现我们上面图中标出的区域并不是最小的,最小的是那个< ul >。


第二步:找到单个目标所在标签

第二步怎么走啊?第一步可以理解吧,第一步做完事要为“find_all”服务的,一篮子全捞出来,那第二步自然是要一个一个拣出来嘛,为“find”服务。
那具体怎么做就不用我再说了吧,参照上一步。

来我带你打开一个标签看一下:

我要偷偷的学Python,然后惊呆所有人(第九天)
看到没,层次分明。


第三步:代码与自动化

第三步自然就要把目标值取出来了嘛,我们顺便把网址也取了吧。

import requests
from bs4 import BeautifulSoup

res = requests.get('http://books.toscrape.com/')

soup = BeautifulSoup(res.text,'html.parser')

items = soup.find('ul',class_ = 'nav nav-list').find('ul').find_all('li')	#我惊奇的发现,还有这种骚操作

for item in items:
    kind = item.find('a')
    print('分类'+kind.text.strip()+'n网址'+kind['href']+'n')

这样打出来你会发现那根本不是一个完整的网址,这要怎么办呢?


第四步:填充网址

其实你打开一个目录,就会发现它的网址长这样:(这里我打开的是第一个目录)
http://books.toscrape.com/catalogue/category/books/travel_2/index.html

这有什么特点呢?咱把它分开:

http://books.toscrape.com/
catalogue/category/books/travel_2/index.html

好,现在再问你看到了什么?
这两部分是不是都能找到出处!!

好,现在我们微调一下上面的代码:

import requests
from bs4 import BeautifulSoup

url = 'http://books.toscrape.com/'

res = requests.get(url)

soup = BeautifulSoup(res.text,'html.parser')

items = soup.find('ul',class_ = 'nav nav-list').find('ul').find_all('li')	#我惊奇的发现,还有这种骚操作

for item in items:
    kind = item.find('a')
    print('分类:'+kind.text.strip()+'n网址:'+url+kind['href']+'n')

第一题到此告一段落、


小爬虫扑空啦

学完这些之后,我就想着去爬我的CSDN评论了。不过一顿操作下来:

目标网址:https://lion-wu.blog.csdn.net/article/details/108858689
标题:MySQL见闻录 – 入门之旅
目标:评论区

gogogo!!!

好,定位代码段:
我要偷偷的学Python,然后惊呆所有人(第九天)
好,层层爬取(演示效果,不然我才不一层一层拨开):
我要偷偷的学Python,然后惊呆所有人(第九天)

好,结果显示为空。

可以去打印出爬下来的网页源代码:res,然后翻一翻,你会惊奇的发现,评论部分被隐藏了!!!

那怎么办呢?接下来那就进入我们今天的第一个知识点了–json串。


json串

依旧是别人的栗子,我来讲给大家懂。

网页源代码里没有我们想要的数据,那它究竟藏到了哪里呢?
想找到答案,需要用到一项新技能——翻找Network!

还记得我一开始就叫大家用谷歌浏览器吗?现在就体现出优势了。

Network

首先,打开一个界面,这里我选择了志炫的歌单,我比较喜欢他的歌。
小白请跟我来,因为你并不知道哪些网页是用json 传输什么数据的,所以练习的时候不要自己乱找网页。
https://y.qq.com/portal/search.html#page=1&searchid=1&remoteplace=txt.yqq.top&t=song&w=%E6%9E%97%E5%BF%97%E7%82%AB

我要偷偷的学Python,然后惊呆所有人(第九天)

这个界面应该会打开吧,怕大家看不到,我特地多圈了几圈,兄弟们,点它、

好,然而并没有发生什么。

那不急,我们重新加载一下这个歌单界面,找到空白处,右击,重新加载。

我要偷偷的学Python,然后惊呆所有人(第九天)

这时候你会看到这么一个界面。
你用别的浏览器试试,会是什么效果。我用火狐试过,找是可以找,一片乱码而已。

Network的功能是:记录在当前页面上发生的所有请求。现在看上去好像空空如也的样子,这是因为Network记录的是实时网络请求。现在网页都已经加载完成,所以不会有东西。

我们点击一下刷新,浏览器会重新访问网络,这样就会有记录。

好,走到这里了,我觉得我有必要介绍一下这个页面上几个比较重要的东西。

重要图标介绍

我要偷偷的学Python,然后惊呆所有人(第九天)

从左往右看啊,红色的圆钮是启用Network监控(默认高亮打开),灰色圆圈是清空面板上的信息。右侧勾选框Preserve log(放大镜旁边那个),它的作用是“保留请求日志”。如果不点击这个,当发生页面跳转的时候,记录就会被清空。所以,我们在爬取一些会发生跳转的网页时,会点亮它。

再往右是对请求进行分类查看。我们最常用的是:ALL(查看全部)/XHR(仅查看XHR)

我要偷偷的学Python,然后惊呆所有人(第九天)

哎,不废话了,上操作流程吧。

操作流程

首先,我们要找的东西是文本嘛,那怎么办呢?直接点XHR就好了。

我要偷偷的学Python,然后惊呆所有人(第九天)

好,现在这么多东西了,我就直接跟你说要的东西就在这里面,你要怎么找?一个一个点开吗?你会发现很多都是传一些边角料,再然后,你会发现那些边角料都比目标文案要小很多,所以就直接找size最大的那个点进去就好。

我要偷偷的学Python,然后惊呆所有人(第九天)

咱也不绕弯子了,进去之后直接点Preview
我要偷偷的学Python,然后惊呆所有人(第九天)

好,Preview点进去之后自己玩一玩,看看里面都是些啥。

看完之后,回来,跟我点开旁边的Headers。
我要偷偷的学Python,然后惊呆所有人(第九天)

好,看到了什么?一个网址是吧,复制它,打开它,是不是和Preview里面的一模一样,只是排版乱了些。

我就不贴了啊,密集恐惧症就别点开了。

这说明什么?这说明我们要爬的网址其实是这个。

注:如果这个网址打不开,那就不用爬了,人家并不想让你爬。

那么,对于这份XHR来说:这个XHR是一个字典,键data对应的值也是一个字典;在该字典里,键song对应的值也是一个字典;在该字典里,键list对应的值是一个列表;在该列表里,一共有20个元素;每一个元素都是一个字典;在每个字典里,键name的值,对应的是歌曲名。

会不会乱?我觉得不会啊,除非你没有一步一步实操跟进。


讲到这里还没有讲到 json串 啊,你先把这个网页爬出来,打印出来看看,是一个又有点像字典,又有点像字符串的玩意儿。
这玩意儿就是json串了。

json

why json?

答案很简单,因为不是所有的编程语言都能读懂Python里的数据类型(如,列表/字典),但是所有的编程语言,都支持文本(比如在Python中,用字符串这种数据类型来表示文本)这种最朴素的数据类型。

如此,json数据才能实现,跨平台,跨语言工作。

而json和XHR之间的关系:XHR用于传输数据,它能传输很多种数据,json是被传输的一种数据格式。就是这样而已。

我们总是可以将json格式的数据,转换成正常的列表/字典,也可以将列表/字典,转换成json。

how json?

方法很简单,请求到数据之后,使用json()方法即可成功读取。接下来的操作,就和列表/字典相一致。

import requests
# 引用requests库
res_music = requests.get('https://c.y.qq.com/soso/fcgi-bin/client_search_cp?ct=24&qqmusic_ver=1298&new_json=1&remoteplace=txt.yqq.song&searchid=67818388354301120&t=0&aggr=1&cr=1&catZhida=1&lossless=0&flag_qc=0&p=1&n=10&w=%E6%9E%97%E5%BF%97%E7%82%AB&g_tk_new_20200303=5381&g_tk=5381&loginUin=0&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8&notice=0&platform=yqq.json&needNewCode=0')
# 调用get方法,下载这个字典
json_music = res_music.json()
# 使用json()方法,将response对象,转为列表/字典
print(json_music)
# 打印json_music的数据类型

所以接下来怎么办呢?

import requests
# 引用requests库
res_music = requests.get('https://c.y.qq.com/soso/fcgi-bin/client_search_cp?ct=24&qqmusic_ver=1298&new_json=1&remoteplace=txt.yqq.song&searchid=67818388354301120&t=0&aggr=1&cr=1&catZhida=1&lossless=0&flag_qc=0&p=1&n=10&w=%E6%9E%97%E5%BF%97%E7%82%AB&g_tk_new_20200303=5381&g_tk=5381&loginUin=0&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8&notice=0&platform=yqq.json&needNewCode=0')
# 调用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']+'.htmlnn')
    # 查找播放链接

小爬虫又来啦

这回,通过我们的一顿操作猛如虎,可算是找对了网址啊:
https://blog.csdn.net/phoenix/web/v1/comment/list/108858689?page=1&size=10&commentId=
这是第一页的评论网址。

好极,我们开始吧。

import requests

res = requests.get('https://blog.csdn.net/phoenix/web/v1/comment/list/108858689?page=1&size=10&commentId=')
# 发起请求,填入请求头和参数

print(res.status_code)

print(res.text)

好极,就试了下水就让人给怼回来了。。。

莫非今天还真爬不过去了?

不知道,再说吧。。


易容术:请求头Request Headers

服务器可能会对我们这些“投机取巧”的爬虫做限制处理。一来可以降低服务器的访问压力,毕竟成千上万次的访问对代码来说就是一个for循环的事儿;二来可以拦截那些想要通过爬虫窃取数据的竞争者。

那这就有一个问题,服务器怎么判断访问者是一个普通的用户(通过浏览器),还是一个爬虫者(通过代码)呢?

这需要我们回到浏览器中,重新认识一个新的信息栏:请求头Request Headers。

什么是Request Headers

看下面这张图

我要偷偷的学Python,然后惊呆所有人(第九天)

每一个请求,都会有一个Request Headers,我们把它称作请求头。它里面会有一些关于该请求的基本信息,比如:这个请求是从什么设备什么浏览器上发出?这个请求是从哪个页面跳转而来?
如上图,user-agent(中文:用户代理)会记录你电脑的信息和浏览器版本,如果我们想告知服务器,我们不是爬虫,而是一个正常的浏览器。就要去修改user-agent。倘若不修改,那么这里的默认值就会是Python,会被服务器认出来。

origin(中文:源头)和referer(中文:引用来源)则记录了这个请求,最初的起源是来自哪个页面。它们的区别是referer会比origin携带的信息更多些。
对于爬取某些特定信息,也要求你注明请求的来源,即origin或referer的内容。


如何添加Request Headers

import requests
url = 'https://blog.csdn.net/phoenix/web/v1/comment/list/108858689?page=1&size=10&commentId='

headers = {
    'origin':'https://lion-wu.blog.csdn.net',
    # 请求来源,本案例中其实是不需要加这个参数的,只是为了演示
    'referer':'https://lion-wu.blog.csdn.net/article/details/108858689',
    # 请求来源,携带的信息比“origin”更丰富,本案例中其实是不需要加这个参数的,只是为了演示
    'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36'
    # 标记了请求从什么设备,什么浏览器上发出
    }
# 伪装请求头

res = requests.get(url,headers=headers)

好极,干!!!


进击吧!小爬虫

这次,我给小爬虫进行了一波易容,可能是它长得不符合服务器的审美吧,所以次次碰壁,这次易容之后,不知道有没有长到服务器的审美上去呢?让我们拭目以待吧!!!

import requests
url = 'https://blog.csdn.net/phoenix/web/v1/comment/list/108858689?page=1&size=10&commentId='

headers = {
    'origin':'https://lion-wu.blog.csdn.net',
    # 请求来源,本案例中其实是不需要加这个参数的,只是为了演示
    'referer':'https://lion-wu.blog.csdn.net/article/details/108858689',
    # 请求来源,携带的信息比“origin”更丰富,本案例中其实是不需要加这个参数的,只是为了演示
    'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36'
    # 标记了请求从什么设备,什么浏览器上发出
    }
# 伪装请求头

res = requests.get(url,headers=headers)
print(res.status_code)

print(res.text)
bash:129$ python ~/classroom/apps-2-id-5c3d89848939b4000100e7f5/129/main.py
200
{"code":200,"message":"success","data":{"count":60,"pageCount":6,"floorCount":59,"list":[{"info":{"commentId":13646053,"articleId":108858689,"parentId":0,"postTime":"2020-10-30 11:03:45","content":"删除多张表:自己想 O(&cap;_&cap;)O~","userName":"qq_43055855","digg":2,"diggArr":[],"parentUserName":null,"parentNickName":null,"avatar":"https://profile.csdnimg.cn/C/B/3/3_qq_43055855","nickName":"海海不掉头发","dateFormat":"6天前","tag":"码皇","parentTag":null,"years":null,"vip":null,"vipIcon":null,"companyBlog":null,"companyBlogIcon":null,"flag":null,"flagIcon":null,"levelIcon":null},"sub":

我就截取一点吧,太大了,可以看出来截下来了就好。

别说了,也可以自己去解析一下,这个以我们之前学的解不了。后面我解给你看。


小爬虫被骗啦

当服务器遇上了整容过后的小爬虫,终于“门户大开”,大方的给了一页的数据,一页的数据,一页。。。

但是我要的是全部啊,你就给我一页就想打发我?打发叫花子呢?

那怎么办呢?这个死渣男,小气得很呐,看来又要我们自己动脑筋咯。


带参请求数据

还记得我们最开始是怎么找到评论区的包吗?对,我没说,我是先将页面清空,然后请求访问了第二个页面,这时候就出现了一个新包,用脚指头想都知道那就是第二个页面的包,不过我还是想用第一个页面,于是我就切回去了。

那我们再想想,这些数据我们是在哪里找到的?我不希望看到你们说Preview啊,想清楚啊,想这样说的朋友,给你们一次重新组织语言的机会。

对,明明就是在Headers的General的url里面找到的嘛,Preview怎么爬?对吧。

本来不想多废话,但是我喜欢分析url,所以就多说两句呗。

第一个页面的URL:https://blog.csdn.net/phoenix/web/v1/comment/list/108858689?page=1&size=10&commentId=
第二个页面的URL:https://blog.csdn.net/phoenix/web/v1/comment/list/108858689?page=2&size=10&commentId=
第三个页面的URL:https://blog.csdn.net/phoenix/web/v1/comment/list/108858689?page=3&size=10&commentId=

一目了然了吧,不用我再多放了。

import requests
from bs4 import BeautifulSoup
import json

headers = {
    'origin':'https://lion-wu.blog.csdn.net',
    # 请求来源,本案例中其实是不需要加这个参数的,只是为了演示
    'referer':'https://lion-wu.blog.csdn.net/article/details/108858689',
    # 请求来源,携带的信息比“origin”更丰富,本案例中其实是不需要加这个参数的,只是为了演示
    'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36'
    # 标记了请求从什么设备,什么浏览器上发出
    }
# 伪装请求头

for i in range(5):
    res = requests.get('https://blog.csdn.net/phoenix/web/v1/comment/list/108858689?page='+str(i)+'&size=10&commentId=',headers=headers)
    print(res.status_code)

    soup = BeautifulSoup(res.text,'html.parser')
    json_items = json.loads(soup.text)
    items = json_items['data']['list']

    for item in items:
        print(item['info']['content'])
        print('n'+'----------'+'n')

强行灌输知识点

有时候呢,你会发现你抓取的几个页面不过是在重复(强行灌输知识点)

那就灌一下吧。

我要偷偷的学Python,然后惊呆所有人(第九天)

我也不知道什么是就要用上,反正先写上。

所以,其实我们可以把Query String Parameters里的内容,直接复制下来,封装为一个字典,传递给params。只是有一点要特别注意:要给他们打引号,让它们变字符串。

所以,代码最后可能长这样:

伪代码

import requests
# 引用requests模块
url = 'https://blog.csdn.net/phoenix/web/v1/comment/list/108858689'

for i in range(5):
    'params' = {
    'page': str(i)
	'size': '10'
	'commentId':  
    }
    # 将参数封装为字典
    res_comments = requests.get(url,params=params,)

好极,好极,这篇就到这里啦,爽呐。

下一篇会比较轻松一些,这篇信息量有点大啊。
我要偷偷的学Python,然后惊呆所有人(第九天)
我要偷偷的学Python,然后惊呆所有人(第九天)

匿名

发表评论

匿名网友