Python練習【利用線程池爬取電影網站信息】

功能實現

爬取貓眼電影TOP100(http://maoyan.com/board/4?offset=90)
    1). 爬取內容: 電影名稱,主演, 上映時間,圖片url地址保存到文件中;
    2). 文件名爲topMovie.csv;
    3). 記錄方式:  電影名稱:主演:上映時間:圖片url地址:評分;
    4). 並爬取的信息保存在數據庫中;
    5). 使用多線程/線城池實現;

編程思路

1.利用多線程分配任務
2.編寫單線程的任務實現功能
    (1)獲取指定url頁面信息。
    (2)從指定信息中匹配所需的信息。
    (3)將獲取到的信息分別保存至文件並寫入數據庫中。

1.利用多線程分配任務

from multiprocessing.pool import ThreadPool
def main():
    # 需要訪問10個分頁,生成對應的urls列表
    urls = [getPageInfo('https://maoyan.com/board/4?offset=%s' % i)for i in range(0,91,10)]
    pool = ThreadPool(10)   # 創建線程池對象,上限爲10個
    pool.map(getInfoInPage,urls) # 獲取信息並保存至文件
    pool.close() # 關閉線程池
    pool.join() # 等待子線程結束

2.單線程任務實現

獲取頁面信息

# 獲取頁面信息
def getPageInfo(url):
    pageObj = urlopen(url)
    pageInfo = pageObj.read().decode('utf-8')
    return pageInfo

保存頁面信息到文件moviePage

page = 0 # 定義全局變量便於分頁存儲
def savePageInfo(pageInfo):
    global page
    page += 1
    with open('doc/moviePage%s'%(page),'w',encoding='utf-8')as f:
        f.write(pageInfo)
    return pageInfo

建立數據庫連接

def connetion():
    return pymysql.connect(
        host='localhost',
        user='root',
        password='mysql',
        database='topMovie',
        charset='utf8',
        autocommit=True
    )

從單個頁面代碼中獲取所需數據

def getInfoInPage(page1):
    # 將獲取的html源碼加工爲美味湯(便於獲取對應的信息)
    soup = BeautifulSoup(page1, 'html.parser')
        # 遍歷單個頁面中 class 爲 content 的div標籤
    for page in soup.find_all('div', {'class': "content"}):
            # 遍歷content節點中的dd標籤
        for movie in page.find_all('dd'):
            # 將soup節點轉化成字符串
            movieInfo = str(movie)
            # 篩選出需要的信息
            name = movie.find('p', {'class': "name"}).text # 獲取電影名稱
            star = re.findall(r'主演:(.*?)\s', movieInfo)[0] # 獲取主演
            releaseTime = re.findall(r'上映時間:(.*?)<', movieInfo)[0] # 獲取上映時間
            imgUrl = re.findall(r'"(http.*?)"', movieInfo)[0] # 獲取宣傳圖片地址
            score = movie.find('p', {'class': "score"}).text # 獲取評分
            # 電影名稱: 主演:上映時間: 圖片url地址:評分;
            oneMovieInfo = '\n{0}:{1}:{2}:{3}:{4}'.format(name, star, releaseTime, imgUrl, score)
            # 寫入文件
            with open('doc/topMovie.csv', 'a+', encoding='utf-8')as f:
                f.write(oneMovieInfo)
            # 單個電影信息存入數據庫的sql語句
            insertSql = 'insert into topmovie(電影名稱,主演,上映時間,圖片地址,評分) value("{0}","{1}","{2}","{3}","{4}")'.format(name, star, releaseTime, imgUrl, score)
            lock.acquire() # 加上線程鎖,防止多線程公用連接出現問題
            cur.execute(insertSql) # 執行插入語句
            lock.release() # 解鎖

多線程公用數據庫的安全問題

多線程公用數據庫如果不進行安全處理,有時會因爲

數據來不及回滾而其他線程進行數據操作,從而導致存儲出現問題

可以通過一下幾種方式調整:

1.讓每個線程擁有自己的連接。

2.利用線程鎖來保證單次操作的完整性。

這裏採用第二種方式在屬性插入語句前後進行加鎖和解鎖操作
            lock.acquire() # 加上線程鎖,防止多線程公用連接出現問題
            cur.execute(insertSql) # 執行插入語句
            lock.release() # 解鎖

修改後的主函數

def main():
    with open('doc/topMovie.csv','w',encoding='utf-8')as f:
            f.write('電影名稱: 主演:上映時間: 圖片url地址:評分;')
    # 創建連接和遊標
    global cur
    global lock
    conn = connetion()
    cur = conn.cursor()
    lock = threading.Lock()
    # 刪除並創建一個新表(刷新每次寫入的數據)
    dropSql = 'drop table topMovie;'
    cur.execute(dropSql)
    createSql = 'create table topMovie(電影名稱 varchar(100),主演 varchar(100),上映時間 varchar(100),圖片地址 varchar(100),評分 varchar(100))default charset=utf8'
    cur.execute(createSql)
    # 生成分頁的urls
    urls = [getPageInfo('https://maoyan.com/board/4?offset=%s' % i)for i in range(0,91,10)]
    # 實現多線程
    pool = ThreadPool(10)
    pool.map(getInfoInPage,urls) # 獲取信息並保存至文件
    # 關閉線程池並等待子線程結束
    pool.close()
    pool.join()
    # 關閉遊標和連接
    cur.close()
    conn.close()

完整代碼

import re
import threading
import time
from multiprocessing.pool import ThreadPool
import pymysql
from bs4 import BeautifulSoup
from urllib.request import urlopen
# 獲取頁面信息
def getPageInfo(url):
    pageObj = urlopen(url)
    pageInfo = pageObj.read().decode('utf-8')
    return pageInfo
page = 0
# 保存頁面信息到文件moviePage
def savePageInfo(pageInfo):
    global page
    page += 1
    with open('doc/moviePage%s'%(page),'w',encoding='utf-8')as f:
        f.write(pageInfo)
    return pageInfo

# 計時器
def timeCounter(fun):
    def wrapper(*args,**kwargs):
        startTime = time.time()
        res = fun(*args,**kwargs)
        endTime = time.time()
        print(fun.__name__+'使用時間爲%.2f'%(endTime-startTime))
        return res
    return wrapper

# 建立數據庫連接
def connetion():
    return pymysql.connect(
        host='localhost',
        user='root',
        password='mysql',
        database='topMovie',
        charset='utf8',
        autocommit=True
    )

# 從單頁源碼中獲取所需要的信息,並分別存至數據庫和文件參數爲頁面源碼(str)
def getInfoInPage(page1):
    soup = BeautifulSoup(page1, 'html.parser')
    for page in soup.find_all('div', {'class': "content"}):
        for movie in page.find_all('dd'):
            # 將soup節點轉化成字符串
            movieInfo = str(movie)
            # 篩選出需要的信息
            name = movie.find('p', {'class': "name"}).text
            star = re.findall(r'主演:(.*?)\s', movieInfo)[0]
            releaseTime = re.findall(r'上映時間:(.*?)<', movieInfo)[0]
            imgUrl = re.findall(r'"(http.*?)"', movieInfo)[0]
            score = movie.find('p', {'class': "score"}).text
            # 電影名稱: 主演:上映時間: 圖片url地址:評分;
            oneMovieInfo = '\n{0}:{1}:{2}:{3}:{4}'.format(name, star, releaseTime, imgUrl, score)
            # 寫入文件
            with open('doc/topMovie.csv', 'a+', encoding='utf-8')as f:
                f.write(oneMovieInfo)
            # 存入數據庫
            insertSql = 'insert into topmovie(電影名稱,主演,上映時間,圖片地址,評分) value("{0}","{1}","{2}","{3}","{4}")'.format(name, star, releaseTime, imgUrl, score)
            lock.acquire()
            cur.execute(insertSql)
            lock.release()
            print(oneMovieInfo)

@timeCounter
def main():
    with open('doc/topMovie.csv','w',encoding='utf-8')as f:
        f.write('電影名稱: 主演:上映時間: 圖片url地址:評分;')
    # 創建連接和遊標
    conn = connetion()
    global cur
    cur = conn.cursor()
    global lock
    lock = threading.Lock()
    # 創建一個新表
    dropSql = 'drop table topMovie;'
    cur.execute(dropSql)
    createSql = 'create table topMovie(電影名稱 varchar(100),主演 varchar(100),上映時間 varchar(100),圖片地址 varchar(100),評分 varchar(100))default charset=utf8'
    cur.execute(createSql)
    urls = [getPageInfo('https://maoyan.com/board/4?offset=%s' % i)for i in range(0,91,10)]
    pool = ThreadPool(10)
    # pool.map(savePageInfo,urls)
    pool.map(getInfoInPage,urls) # 獲取信息並保存至文件
    pool.close()
    pool.join()
    cur.close()
    conn.close()

顯示結果

數據庫

Python練習【利用線程池爬取電影網站信息】

文件

Python練習【利用線程池爬取電影網站信息】

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