社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

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

看,未来 • 4 年前 • 514 次点击  

在这里插入图片描述

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

前言

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

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

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

在这里插入图片描述
插播一条推送:(如果是小白的话,可以看一下下面这一段)

欢迎来到我们的圈子

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

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

直通群的传送门: 传送门


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

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

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

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

开手练习:爬取网上书店

目标网址:http://books.toscrape.com/
任务:爬取目标网址中的分类目录:
在这里插入图片描述

干!

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

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

这个会找吧:
在这里插入图片描述

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

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


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

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

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

在这里插入图片描述
看到没,层次分明。


第三步:代码与自动化

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

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')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

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


第四步:填充网址

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

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

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

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

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

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')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

第一题到此告一段落、


小爬虫扑空啦

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

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

gogogo!!!

好,定位代码段:
在这里插入图片描述
好,层层爬取(演示效果,不然我才不一层一层拨开):
在这里插入图片描述

好,结果显示为空。

可以去打印出爬下来的网页源代码: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

在这里插入图片描述

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

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

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

在这里插入图片描述

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

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

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

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

重要图标介绍

在这里插入图片描述

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

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

在这里插入图片描述

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

操作流程

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

在这里插入图片描述

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

在这里插入图片描述

咱也不绕弯子了,进去之后直接点Preview
在这里插入图片描述

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

看完之后,回来,跟我点开旁边的Headers。
在这里插入图片描述

好,看到了什么?一个网址是吧,复制它,打开它,是不是和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的数据类型
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

所以接下来怎么办呢?

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']+'.html\n\n')
    # 查找播放链接
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

小爬虫又来啦

这回,通过我们的一顿操作猛如虎,可算是找对了网址啊:
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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

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

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

不知道,再说吧。。


易容术:请求头Request Headers

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

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

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

什么是Request Headers

看下面这张图

在这里插入图片描述

每一个请求,都会有一个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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

好极,干!!!


进击吧!小爬虫

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

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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
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":


    

  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

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

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


小爬虫被骗啦

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

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

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


带参请求数据

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

那我们再想想,这些数据我们是在哪里找到的?我不希望看到你们说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=
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

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

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')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

强行灌输知识点

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

那就灌一下吧。

在这里插入图片描述

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

所以,其实我们可以把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,)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

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

下一篇会比较轻松一些,这篇信息量有点大啊。
在这里插入图片描述
在这里插入图片描述

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/74961
 
514 次点击