正则表达式和豆瓣Top250的爬取练习

datawhale任务2-爬取豆瓣top250


正则表达式

正则表达式的功能用于实现字符串的特定模式精确检索或替换操作。

常用匹配模式

模式 描述
\w 匹配字母、数字及下划线
\W 匹配不是字母、数字或下划线的字符
\s 匹配任意空白字符,等价于[\t\n\r\f]
\S 匹配任意非空白字符
\d 匹配任意数字,等价于[0-9]
\D 匹配任意非数字的字符
\A 匹配字符串开头
\Z 匹配字符串结尾,如果存在换行,只匹配到换行前的结束字符串
\z 匹配字符串结尾,如果存在换行,同时还会匹配换行符
\G 匹配最后匹配完成的位置
\n 匹配一个换行符
\t 匹配一个制表符
^ 匹配一行字符串的开头
$ 匹配一行字符串的结尾
. 匹配任意字符,除了换行符,添加修饰符,可匹配任意字符
[...] 用来表示一组字符,单独列出,比如[amk]则匹配a,m,k
[^...] 不在[]中的字符,比如[^abc],即除了a,b,c之外的字符
* 匹配0个或多个表达式
+ 匹配1个或多个表达式
? 匹配0个或1个前面的正则表达式定义的片段,非贪婪方式
{n} 精确匹配n个前表达式
{n, m} 匹配n到m次由前面正则表达式定义的片段,贪婪方式
a 管道符 b 匹配a或b
() 匹配括号内的表达式,也表示一个组

常用修饰符

修饰符 描述
re.I 使匹配对大小写不敏感
re.L 做本地化识别匹配
re.M 多行匹配,影响^$
re.S 使.匹配包括换行在内的所有字符
re.U 根据Unicode字符集解析字符,这个标志影响\w,\W,\b\B
re.X 该标志通过给予你更灵活的格式 以便你将正骨则表达式写着更易于理解

基本方法

  • match():以表达式开头进行匹配,一旦开头不匹配,整个匹配就会失败,返回None
  • search():扫描整个字符串,返回第一个成功匹配的字符中,否则返回None
  • findall():搜索整个字符串,返回匹配成功的所有字符串,否则返回空列表

豆瓣250页面分析

豆瓣Top250是包含250部电影,此次爬取的数据为之250部电影的排名,电影名,导演及国家。

url为:https://movie.douban.com/top250?start=0,默认每页有25部电影信息。
每往后翻一页,url中start参数每次即增加25,即250部电影,10页。

豆瓣有反爬虫,如果过多请求会被封,所以先建立一个文件夹用于存储以爬取的网页,如果下次爬取本地有相应的文件,就不会发起网络请求,从而节省下时间,也不会吸引豆瓣反爬系统的注意。

    if 'cached' not in os.listdir('./'):
        os.system('mkdir cached')
    listdir = os.listdir("./cached")
    path = './cached/'
def download(index, url, listdir, path):
    # 下标,目标网址,已缓存的文件,文件保存路径
    target_file = "{}.html".format(index)
    target_url = url.format(index * 25)
    if target_file not in listdir:
        # 对应的 html 没有下载下来
        html = requests.get(target_url, headers=headers)
        html.encoding = 'utf-8'
        with open(path + target_file, 'w+') as file:
            file.write(html.text)
        html = html.text
    else:
        with open(path + target_file, 'r') as file:
            html = file.read()
    return html

要爬取的信息是排名,电影名,导演,国家

排名在字符串的位置如下
通过浏览器的开发者面板看到是这样的

实际是这样的

电影名位于<span class='title'>XXX</span>中,其中有些有两个<span class='title'>,为避免数据混乱,故只采用一个第一个title,且导演名在信息中的相对位置是肯定的且每部电影都有导演的相关信息,在<p class=""></p>中,故将三都的信息均写于一个正则表达式

three_pattern = re.compile(('<em class="">(.*?)</em>.*?<span class="title">(.*?)</span>'
                      '.*?<p class="">.*?导演:(.*?)&nbsp;'), re.S)

刚开始时原是把国家的正则匹配也写在一起,后来发现那样会导致有些数据无法匹配,所以分开写,如下

nation_pattern = re.compile('<br>.*?/&nbsp;(.*?)&nbsp;/.*?</p>', re.S)

完整代码

"""
datawhale爬虫任务2
    任务:爬取豆瓣电影250
分析 URL,有一个GET参数,start每次变化增加250
https://movie.douban.com/top250?start=0
鉴于豆瓣电影会反爬虫,故将内容先爬下本地,再分析
"""
import requests
import os
import re


# (提取名次,片名,导演),(国家)
three_pattern = re.compile(('<em class="">(.*?)</em>.*?<span class="title">(.*?)</span>'
                      '.*?<p class="">.*?导演:(.*?)&nbsp;'), re.S)
nation_pattern = re.compile('<br>.*?/&nbsp;(.*?)&nbsp;/.*?</p>', re.S)


headers = {
    'User-Agent': ('Mozilla/5.0 (X11; Linux x86_64)'
                   ' AppleWebKit/537.36 (KHTML, like Gecko)'
                   ' Chrome/68.0.3440.106 Safari/537.36')
}


class Movie(object):
    def __init__(self, rnd, n):
        # rnd: 名次,名字,导演
        # n:   国家
        self.info = {
            '排名': rnd[0],
            '电影名': rnd[1],
            '导演': rnd[2],
            '国家': n
        }

    def __repr__(self):
        return str(self.info)


def download(index, url, listdir, path):
    # 下标,目标网址,已缓存的文件,文件保存路径
    target_file = "{}.html".format(index)
    target_url = url.format(index * 25)
    if target_file not in listdir:
        # 对应的 html 没有下载下来
        html = requests.get(target_url, headers=headers)
        html.encoding = 'utf-8'
        with open(path + target_file, 'w+') as file:
            file.write(html.text)
        html = html.text
    else:
        with open(path + target_file, 'r') as file:
            html = file.read()
    return html


def parse(html):
    # 分析面页面,提取数据
    # 名次,影片名称,导演
    rank_name_dire = re.findall(three_pattern, html)
    # 国家
    nation = re.findall(nation_pattern, html)
    info = [Movie(i[0], i[1]) for i in zip(rank_name_dire, nation)]
    for i in info:
        print(i)


def main():
    if 'cached' not in os.listdir('./'):
        os.system('mkdir cached')
    listdir = os.listdir("./cached")
    path = './cached/'
    url = 'https://movie.douban.com/top250?start={}'
    for i in range(10):
        html = download(i, url, listdir, path)
        parse(html)
    print("结束")


if __name__ == '__main__':
    main()


参考资料

posted @ 2019-03-02 15:35  朝行  阅读(1200)  评论(0编辑  收藏  举报