之前两篇已经说完了如何爬取网页以及如何解析其中的数据, 那么今天我们就可以开始第一次实战了.
这篇实战包含两个内容.
* 利用爬虫调用 API 来解析照片的拍摄位置
* 利用爬虫爬取 Bilibili 视频中的弹幕
关于爬虫调用 API 这一说法, 其实就是通过 get 或者 post 请求携带着参数, 将内容发给对方服务器, 服务器会根据请求的 API 是哪个来进行处理.
比如说 / delete?id=2 和 / save?id=1&name=antz 这两个请求就分别是删除 id 等于 2 的数据, 保存一条 id 等于 1 姓名为 antz 的数据.
此时我们就只需要向对方服务器发送出这个请求就可以了, requests.get(url)就这么简单.
一. 根据照片解析定位
不知道你平常拍照片有没有在你手机的设置里仔细查看过, 比如说下面这个选项[地理位置] .
打开它之后, 我拍的照片上也没有显示地理位置啊?
这是因为这些数据被放在了照片文件数据里面, 可能你很难理解, 你可以回想一下之前我们说的 get 之后的响应, 响应体分为响应头和响应体. 照片的数据也是一样, 有信息头 (随便叫的) 和数据体, 信息头里面有你这张照片的各种信息, 拍摄时间地点设备等, 而数据体就是你用看图软件打开时显示在你眼中的那些了, 我们平常关注到的只有数据体, 只关心照片是不是好看, 而信息头的内容对我们来说其实无关紧要.
下面是我之前拍的一张照片经过解析获得的信息头数据, 从里面可以看到照片尺寸, 我的 Make 设备是 HUAWEI, 我的镜头数据, 时间等等.
在 Python 中我们可以使用 exifread 库来解析图片的头信息.
pip install exifread
调用 exifread 的 process_file 方法, 参数传入 file, 就可以得到照片的 exif 信息了.
img_exif = exifread.process_file(open("路径", 'rb'))
返回的 img_exif 是一个字典数据, 我们可以直接根据上面那张头数据里面的标题来拿到对应的数据.
- # 纬度数
- latitude_gps = img_exif['GPS GPSLatitude']
- # N,S 南北纬方向
- latitude_direction = img_exif['GPS GPSLatitudeRef']
- # 经度数
- longitude_gps = img_exif['GPS GPSLongitude']
- # E,W 东西经方向
- longitude_direction = img_exif['GPS GPSLongitudeRef']
这样我们就拿到南北纬以及经纬度了.
那么如何根据这些数据来获得定位呢?
我们可以利用高德地图开放的 API 来将经纬度转换为我们能够直接读懂的位置信息.
url = 'https://restapi.amap.com/v3/geocode/regeo?key={}&location={}'
上面是高德地图逆向解析定位的 API 地址, key 需要在高德地图官网申请开发者之后拿到(很容易),location 就是经纬度南北纬信息了.
- resp = requests.get(url.format(api_key, location))
- location_data = JSON.loads(resp.text)
- address = location_data.get('regeocode').get('formatted_address')
上面的代码是不是已经很简单了, requests.get 请求 API 拿到对方返回的结果, 返回的数据的 JSON 数据, 所以 loads 解析之后直接拿 address 即可.
这样我们就可以拿到这张照片的拍摄地点了.
二. Bilibili 弹幕爬虫
先来思考一个问题, B 站一个视频的弹幕最多会有多少?
比较多的会有 2000 条吧, 这么多数据, B 站肯定是不会直接把弹幕和这个视频绑在一起的.
也就是说, 有一个视频地址为 https://www.bilibili.com/video/av67946325, 你如果直接去 requests.get 这个地址, 里面是不会有弹幕的, 回想第一篇说到的携程异步加载数据的方式, B 站的弹幕也一定是先加载当前视频的界面, 然后再异步填充弹幕的.
接下来我们就可以打开火狐浏览器 (平常可以火狐谷歌控制台都使用, 因为谷歌里面因为插件被拦截下来的包在火狐可以抓到, 同理谷歌也是) 的控制台来观察网络请求了.
经过仔细排查之后, 我找到了一个请求 xml 的, 它后面跟了一个 oid, 查看它的响应内容之后可以发现它就是弹幕文件.
它的响应时间 98 毫秒, 远超其它几个响应, 所以说如果把弹幕直接放在视频页面, 用户体验一定会很差.
找到弹幕了, 爬取它很容易, 但是我们想要是爬取固定 av 号视频的弹幕, 而不是说随意去找一个 oid 来爬取弹幕, 这样我们都不知道爬下来的弹幕是哪个视频的.
接下来我们就可以复制 oid 的 117784982 值, 去视频页面搜索看看了, 通过视频来获得它的 oid 再来爬 xml 弹幕就很方便了.
这次用了谷歌浏览器, 在里面通过搜索 oid 果然搜索到相关的数据了.
其中 cid 是弹幕对应的 id,aid 对应视频 av 号.
先把这个页面爬取下来.
- # encoding: utf-8
- import requests
- headers = {
- 'User-Agent':'Mozilla/5.0 (X11; Linux x86_64) ApplewebKit/537.36 (Khtml, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
- 'Accept': 'text/html',
- 'Cookie': "_uuid=1DBA4F96-2E63-8488-DC25-B8623EFF40E773841infoc; buvid3=FE0D3174-E871-4A3E-877C-A4ED86E20523155831infoc; LIVE_BUVID=AUTO8515670521735348; sid=l765gx48; DedeUserID=33717177; DedeUserID__ckMd5=be4de02fd64f0e56; SESSDATA=cf65a5e0,1569644183,c4de7381; bili_jct=1e8cdbb5755b4ecd0346761a121650f5; CURRENT_FNVAL=16; stardustvideo=1; rpdid=|(umY))|ukl~0J'ulY~uJm)kJ; UM_distinctid=16ce0e51cf0abc-02da63c2df0b4b-5373e62-1fa400-16ce0e51cf18d8; stardustpgcv=0606; im_notify_type_33717177=0; finger=b3372c5f; CURRENT_QUALITY=112; bp_t_offset_33717177=300203628285382610"
- }
- resp = requests.get('https://www.bilibili.com/video/av67946325',headers=headers)
- print(resp.text)
拿到了内容我们就要从中解析弹幕 id 了, 对于这种规则紊乱的网页, 我们就不能用上一篇中 Bs4 解析了, 而是使用正则表达式.
正则表达式最简单的使用方式其实就是直接 match.
re.search(匹配规则, 文本).group()
观察这里的内容, 我们大致的匹配规则就有了.
cid={目标}&aid=av 号
117784982 就是我们的目标.
- av_id = '67946325'
- resp = requests.get('https://www.bilibili.com/video/av'+av_id,headers=headers)
- match_rule = r'cid=(.*?)&aid'
- oid = re.search(match_rule,resp.text).group().replace('cid=','').replace('&aid','')
- print('oid='+oid)
先根据 av 号拿到视频页面, 然后解析视频页面拿到 oid, 最后用 oid 去请求 xml 弹幕文件.
- xml_url = 'https://api.bilibili.com/x/v1/dm/list.so?oid='+oid
- resp = requests.get(xml_url,headers=headers)
- print(resp)
这样我们就完成 B 站弹幕爬虫了.
来源: https://www.cnblogs.com/LexMoon/p/pyspider03.html