Python練習【利用線程池爬取電影網站信息】
阿新 • • 發佈:2019-02-11
blog name insert page 處理 RoCE 獲取信息 mat etime 功能實現
爬取貓眼電影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練習【利用線程池爬取電影網站信息】