多進程爬取豆瓣電影Top250並存入MongoDB數據庫

該爬蟲大致分爲以下步驟:

1. 獲取豆瓣電影 Top250 頁面的 10 個分頁的 URL
2. 通過解析 10 個分頁的 HTML 獲取每個分頁 25 部電影的 URL
3. 獲取每部電影的 HTML 代碼並解析提取電影信息
4. 將提取的信息保存爲 dict 形式並存入 MongoDB 數據庫
5. 設計多進程爬蟲以提高效率

一、首先分析 10 個分頁的 URL 規律

進入第二頁之後,查看此時的URL爲 https://movie.douban.com/top250?start=25&filter=

進入第二頁之後,查看此時的URL爲 https://movie.douban.com/top250?start=50&filter=

……

顯然此處通過 start 參數來控制分頁,因此我們通過如下代碼來獲取 10 個分頁:

def get_html(url):
    """
    @功能: 爬取網頁並獲取其HTML代碼
    @參數: 網頁URL
    @返回: 網頁的HTML代碼
    """
    logging.info('scraping %s...', url)
    try:
        response = requests.get(url, headers=random.choice(headers))
        if response.status_code == 200:
            return response.text
        logging.error('get invalid status code %s while scraping %s', response.status_code, url)
    except requests.RequestException:
        logging.error('error occurred while scraping %s', url, exc_info=True)


def get_page_html(index):
    """
    @功能: 獲取外部頁面的HTML代碼
    @參數: 頁面索引
    @返回: 外部頁面的HTML代碼
    """
    page_url = f"https://movie.douban.com/top250?start={index}&filter="
    return get_html(page_url)

二、解析分頁的 HTML 並獲取每個分頁 25 部電影的 URL

分析 HTML 代碼並提取出每部電影的URL:

使用 XPath Helper 檢驗一下:
在這裏插入圖片描述

代碼如下:

def get_movies_url(page_html):
    """
    @功能: 從外部頁面獲取每部電影的URL
    @參數: 每個外部頁面的HTML代碼
    @返回: 每個外部頁面的URL
    """
    doc = etree.HTML(page_html)
    for i in range(1,26):
        movie_url = doc.xpath('//ol/li[{}]/div/div[2]/div[1]/a/@href'.format(i))[0] # 提取每部電影的URL
        logging.info('get movie url %s', movie_url)
        yield movie_url

三、解析每部電影並提取信息,然後將提取的信息保存爲 dict 形式

代碼如下:

def parse_movie(movie_html):
    """
    @功能: 解析每部電影的信息並提取
    @參數: 對應電影的HTML代碼
    @返回: dict形式的電影信息
    """
    doc = etree.HTML(movie_html) # 解析電影HTML代碼
    cover = doc.xpath("//div[3]/div[1]/div[3]/div[1]/div[1]/div[1]/div[1]/div[1]/a/img/@src")[0]
    name = doc.xpath("//div[3]/div[1]/h1/span[1]/text()")[0]
    rating_num = doc.xpath("//div[3]/div[1]/div[3]/div[1]/div[1]/div[1]/div[2]/div[1]/div[2]/div/div[2]/a/span/text()")[0]
    date = doc.xpath("//div[3]/div[1]/h1/span[2]/text()")[0]
    info = ''.join(doc.xpath("//div[@class='related-info']/div[@id='link-report']/span/text()")).strip().replace(' ', '').replace('\n', '').replace('\xa0', '').replace(u'\u3000', u' ')
    score = doc.xpath("//div[3]/div[1]/div[3]/div[1]/div[1]/div[1]/div[2]/div[1]/div[2]/strong/text()")[0]
    score = float(score) if score else None
   
    return {
        'cover': cover,
        'name': name,
        'rating_num': rating_num,
        'date': date,
        'info': info,
        'score': score
    }

四、將提取到的電影信息存入 MongoDB 數據庫

MONGO_CONNECTION_STRING = 'mongodb://localhost:27017' # MongoDB的連接字符串
MONGO_DB_NAME = 'movies' # 數據庫名稱
MONGO_COLLECTION_NAME = 'movies1' # 集合名稱

client = pymongo.MongoClient(MONGO_CONNECTION_STRING) # 創建MongoDB的連接對象
db = client['movies'] # 指定數據庫
collection = db['douban_movies'] # 指定集合

def save_to_MongoDB(data):
    """
    @功能: 把電影數據存入MongoDB數據庫
    @參數: dict形式的電影信息
    @返回: 無
    """
    collection.update_one({
        'name': data.get('name')
    }, {
        '$set': data
    }, upsert=True)

五、設計多進程爬蟲以提高效率

def scrape_html(url):
    """
    @功能: 爬取網頁並獲取其HTML代碼
    @參數: 網頁URL
    @返回: 網頁的HTML代碼
    """
    return get_html(url) # 代碼複用,降低耦合度

def main(page_index):
    """
    @功能: 每個線程
    @參數: 電影頁面索引
    @返回: 無
    """
    page_html = get_page_html(page_index)
    movies_url = get_movies_url(page_html)

    for movie_url in movies_url:
        movie_html = scrape_html(movie_url)
        data = parse_movie(movie_html)
        
        logging.info('get data %s', data)
        logging.info('saving data to mongodb')
        save_to_MongoDB(data)
        logging.info('data saved successfully')


if __name__ == '__main__':
    pool = multiprocessing.Pool()
    page_indexs = range(0, 250, 25)
    pool.map(main, page_indexs)
    pool.close()

運行代碼,結果如下:

可以看到電影數據已經成功存入 MongoDB 數據庫,此處使用 Robo 3T 進行可視化。


完整源代碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章