1. 程式人生 > >正則表達式和豆瓣Top250的爬取練習

正則表達式和豆瓣Top250的爬取練習

返回 span 字母 精確 下劃線 ocs utf-8 反爬蟲 [1]

datawhale任務2-爬取豆瓣top250

  • 正則表達式
  • 豆瓣250頁面分析
  • 完整代碼
  • 參考資料

正則表達式

正則表達式的功能用於實現字符串的特定模式精確檢索或替換操作。

  • 常用匹配模式
  • 常用修飾符
  • 基本方法

常用匹配模式

模式 描述
\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()

參考資料

  • 崔慶才(pytho3爬蟲開發實戰》
  • AstralWind的博客園
  • python的re庫文檔

正則表達式和豆瓣Top250的爬取練習