python爬虫进阶--动态网页和正则表达式

标题 python爬虫进阶–动态网页和正则表达式

介绍

上一篇使用最简单的方法爬取了唱吧一些歌曲,本篇介绍如何爬取更多歌曲,主要是以下两个问题。

  1. 如何爬取动态加载的网页数据?
  2. 如何解析出网页内嵌的script代码中的数据?

分析一:

  1. 打开我的唱吧主页,下拉,点击加载更多
    python爬虫进阶--动态网页和正则表达式_第1张图片
  2. 点击加载更多发现url并没有发生变化,但网页确实请求到了更多数据
    python爬虫进阶--动态网页和正则表达式_第2张图片
  3. 查了资料发现这是一种名叫ajax的技术,以下是百度百科的解释
    python爬虫进阶--动态网页和正则表达式_第3张图片
  4. 新的请求在网页审查元素的XHR中可以看到:进入(chrome) F12 -> network -> xhr
    此时点击加载更多
    python爬虫进阶--动态网页和正则表达式_第4张图片
  5. 这里就是网页真实的请求数据,在preview栏可以看到响应的预览信息,在headers中可以看到真实的请求url
    在这里插入图片描述
  6. 分析这个请求url,发现其中有一些get方式提交的参数如:pageNum=1 userid=43649980
    盲猜 一个是页码,一个是用户id,继续点击加载更多,不难发现新的请求url pageNum=2
    手动将pageNum改为0进行访问,发现竟然真的是第0页(默认加载的)的数据

    OK,到了这里就有思路了,接下来开始写代码。

代码实现一

import requests
from bs4 import BeautifulSoup
import time

my_header = {
 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.193 Safari/537.36'
}

url_head = 'http://changba.com/s'
user_id = '43649980'       #用户id
song_list ={}
index = 0
for n in range(2):	#爬取前2页
    url = f'http://changba.com/member/personcenter/loadmore.php?ver=1&pageNum={n}&type=0&userid={user_id}&curuserid=-1'
    res = requests.get(url, headers=my_header)
    # 将response对象转为列表
    json_song = res.json()
    # 循环获取歌曲信息
    for song_info in json_song:
        # 歌曲名称
        song_name = song_info['songname']
        # 歌曲播放页链接
        song_id = song_info['enworkid']
        # 歌曲图片
        song_img = song_info['mvCover']

由于请求得到的数据是json格式,因此需要先转换为可解析的格式
其中json_song = res.json()的意思是将json格式的数据转为列表格式以便进行解析
至此可以获取到的数据有:songname / mvCover / workid / enworkid等
python爬虫进阶--动态网页和正则表达式_第5张图片
可以拿这里的workid像上篇 url = “固定url前缀 + workid + .mp3” 这样去拼接,不过这样只能获取到符合这种规则的歌曲url ,而不符合的就没办法了,接下来就是第二个问题。

分析二

  1. 来到歌曲播放页面:分析url ,发现就是 changba.com/s/ + enworkid 的简单拼接
    (其中 enworkid 是上面已经获取到的)那么我们可以方便的来到该页面,继续请求数据
  2. 分析该页面的源码
    python爬虫进阶--动态网页和正则表达式_第6张图片
    在audio标签中 有歌曲源文件的url ,拿到这个url 直接获取并按照二进制数据保存就是歌曲文件本件,然而现实并没有那么简单,requests.get请求到该页面源码,find查找audio标签,发现里面的src属性是空的,为什么呢?
    在这里插入图片描述
  3. F12进入 network -> all 标签中,刷新网页 此时浏览器重新发起请求,可以看到获取到的响应数据有很多种类,大致就是有图片、html 、css 、 js等
    python爬虫进阶--动态网页和正则表达式_第7张图片
    如图,document 对应html文件,即网页源代码, png是一些图片资源,stylesheet 是css,用来设置网页的样式,script就是 js 代码;实际上html中通常也包含部分css代码和js代码。html+css+js 共同组成前端。浏览器在接收到响应的数据后,会执行js代码,对网页进行渲染,我们看到的网页通常是已经经过浏览器渲染,加载过js代码之后的网页。在网页上点右键,有显示网页源代码和检查两个选项,其中检查与F12是一样的,显示的都是经过浏览器渲染后的网页的代码,而显示网页源代码 跳转查看的是才是真正的未经渲染的网页源代码。那么就不难想到,网页源代码中的 audio标签中的src 开始确实是空值,js代码执行的时候将对应的数据填充上去了,所以我们直接获取到的网页源代码中解析不到 audio的src值。
    说了这么多,该如何获取我们要的url呢。
    查了资料,总结有以下两种方法:
    1. 使用模拟操作浏览器的库如selenium, 它更像是在自动操作浏览器发起请求,获取数据,使用比较简单粗暴,缺点是严重拖慢爬虫速度。
    2. 使用python中类似PyExecJS、PyV8、Js2Py等可以执行js代码的库,直接执行js代码。
      本项目中,使用了另一种取巧的方法(主要是因为菜,嫌弃上面方法1,又不会用方法2):
      因为分析发现,我们的目标就藏在网页源代码内嵌的 js 代码中,只需要将其解析出来即可。
      python爬虫进阶--动态网页和正则表达式_第8张图片

代码实现二

由于目标数据在js代码中,无法像解析普通标签一样将内容解析出来,这里使用正则表达式进行解析,很简单,直接上代码:

# 拼出播放页面的url
        play_url = 'http://changba.com/s/' + song_id
        # 继续爬取
        play_res = requests.get(play_url, headers=my_header)
        # 使用正则表达式,获取播放页中的url
        result = re.findall(r'var a="(.*?)",', play_res.text)
        # 判断是否获取成功
        try:
            res_source_song = requests.get(result[0])
        except Exception as e:
            print('失败2,not found')
            continue
        else:
            if res_source_song.status_code == 200:
                index += 1
                with open(f'./song/{index}' + song_name + '.mp3', 'wb') as song_f:
                    song_f.write(res_source_song.content)
                print(song_name + ' 下载成功')
            # 注意我们是在学习,不是在攻击别人服务器,一定要加个延时
            time.sleep(0.5)

re.findall(r'var a="(.*?)",', play_res.text)

这句代码的意思是使用正则表达式匹配 var a=" 与 ", 之间的所有字符串,通过ctrl + F 在网页源码中搜索可知,这个条件可以精确定位到目标字符串。
至此已经成功获取到想要的数据。

正则表达式

补充正则表达式的常用方法:

  1. 普通字符的搜索和字符串搜索完全一致,所以我们主要看特殊字符的使用
    '''
    普通字符的搜索和字符串搜索完全一致,所以我们主要看特殊字符的使用
    
    说明位置的特殊字符
    -   ^表示行首:     ^hello 匹配 hellohello 中的第一个 hello
    -   $表示行尾       hello$ 匹配 hellohello 中的第二个 hello
    
    说明数量的特殊字符
    -   ?          表示 0 个或者 1 个,例如 ab?c 匹配 ac 和 abc
    -   +          表示 1 个或更多个,例如 ab+c 匹配 abc\abbc\ab...c
    -   *          表示 0 个或更多个,例如 ab*c 匹配 ac\abc\ab...c
    -   {n}         表示匹配 n 个, ab{3}c 只匹配 abbbc
    -   {n,}        表示匹配最少 n 个,+ 对应 {1,},* 对应 {0,}
    -   {n,m}       表示匹配 n 到 m 个,? 匹配 {0,1}
    
    说明类型的特殊字符
    -   [a-zA-Z]    表示一个大小写字母,例如前面的例子匹配任何一个字母
    -   [^a-z]      表示除了小写的字母以外的所有字符
    -   \d \D       \d 对应 [0-9], \D 对应[^0-9]
    -   \s \S       \s 对应 [\n\r\t] \S对应[^\n\r\t]
    -   \w \W       \w 对应 [0-9a-zA-Z_] \W 对应[^0-9a-zA-Z_]
    -   .           表示任意一个字符
    '''
    
  2. re 模块中的函数
    # 尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none。
    re.match(pattern, string, flags=0)
    # re.search 扫描整个字符串并返回第一个成功的匹配。
    re.search(pattern, string, flags=0)
    # re.sub用于替换字符串中的匹配项
    re.sub(pattern, repl, string, count=0, flags=0)
    # 在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。
    findall(string[, pos[, endpos]])
    # split 方法按照能够匹配的子串将字符串分割后返回列表,它的使用形式如下:
    

总结

以上是作为小白的我,近两天学习python爬虫,所学到的一些知识,通过小项目的方式写出来,其中也包含了一些自己的理解,学的比较浅,有不正确的地方欢迎指正,也欢迎有兴趣的朋友多多交流。

你可能感兴趣的:(python爬虫,python,爬虫)