文章目錄
- 多線程實作簡單的爬蟲儲存在字典中(電影天堂)
- 爬取電影資訊儲存在資料庫中
- 将爬取的電影資訊展示到網頁中
多線程實作簡單的爬蟲儲存在字典中(電影天堂)
=============================使用多線程得到下載下傳的電影位址(沒有使用的時候,很慢)==================================
import re
import urllib.request
import time
import threading
class Spider(object):
def __init__(self):
self.film_dict = {} # 定義字典存儲下載下傳的影片資訊
self.i = 1 # 檢視擷取影片的數量
self.lock1 = threading.Lock() # 互斥的寫入字典
def start(self):
# 設定多線程,爬蟲多個頁面清單
for page in range(1,4):
t1 = threading.Thread(target=self.get_movie_link, args=(page,))
t1.start()
# 得到字典對應的數組
list1 = self.film_dict.items()
# 所有線程執行完畢後再退出
while len(threading.enumerate()) != 1:
time.sleep(1)
# 周遊下載下傳字典,擷取影片名稱、下載下傳位址
for film_name, film_download_url in list1:
print(film_name, "|", film_download_url)
def get_movie_link(self, page):
#設定要爬的電影清單url
film_list_url = "https://www.ygdy8.net/html/gndy/dyzz/list_23_%d.html" % page
# 打開url ,擷取資料 <http.client.HTTPResponse object at 0x000002596CA13400>
response_list = urllib.request.urlopen(film_list_url)
# 讀取清單傳回的資料
response_list_data = response_list.read()
# 解碼資料
response_list_text = response_list_data.decode("gb2312", "ignore")#輸出的就是右擊檢視網頁源代碼的内容
# 使用正則比對獲得每一條的電影名和相應連結
# <a href="/html/gndy/dyzz/20200809/60326.html" target="_blank" rel="external nofollow" class="ulink">2020年奇幻劇情《秘密花園》BD中英雙字幕</a>
url_list = re.findall("<a href=\"(.*)\" class=\"ulink\">(.*)</a>", response_list_text)
# url_list 是清單,每一個元素是元組
# [('/html/gndy/dyzz/20200809/60326.html', '2020年奇幻劇情《秘密花園》BD中英雙字幕'),
# ('/html/gndy/dyzz/20200809/60325.html', '2020年劇情傳記《放射性物質》BD中英雙字幕')..]
for film_content_url, film_name in url_list:
film_content_url = "https://www.ygdy8.net" + film_content_url
# 打開電影的url位址
response_content = urllib.request.urlopen(film_content_url)
response_content_data = response_content.read()
response_content_text = response_content_data.decode("gb2312", "ignore")
# 使用正則 取出下載下傳位址
ret = re.search("bgcolor=\"#fdfddf\"><a href=\"(.*?)\">", response_content_text)
if ret:
# 儲存影片名稱和下載下傳位址到字典中
self.lock1.acquire()
self.film_dict[film_name] = ret.group(1)
self.lock1.release()
print("已經成功爬取 %d 個影片位址!" % self.i)
self.i += 1
else:
continue
def main():
film_spider = Spider()
film_spider.start()
if __name__ == '__main__':
main()
#注意:如果沒有‘ignore’會報錯
#UnicodeDecodeError: 'gb2312' codec can't decode byte 0xa9 in position 11775: illegal multibyte sequence
#是以使用ignore 忽略非 gb2321編碼的字元

爬取電影資訊儲存在資料庫中
首先建立資料庫和存儲電影資訊的表
create database movie_db charset=utf8; # 建立資料庫
use movie_db; # 選擇資料庫
# 建立資料表
create table movie_link(
id int(11) primary key auto_increment,
film_name varchar(255) not null,
film_link varchar(255) not null
)charsset=utf8;
=====================簡單爬取電影資訊插入到資料庫中,重複的資料不插入=============================================
import re
import urllib.request
from pymysql import connect
def add_film(film_name, film_link):
""" 定義專門的函數,儲存影片資訊到資料庫中(插入)"""
sql ="insert into movie_link values(null, %s, %s)"
ret = cur.execute(sql, [film_name, film_link])
# 如果插入成功,給出提示
if ret:
print("儲存成功!影片[%s]" % film_name)
def film_exist(film_name, film_link):
"""定義專門的函數,檢測資料是否已經存在(查詢)"""
sql = "select id from movie_link where film_name=%s and film_link=%s limit 1"
ret = cur.execute(sql,[film_name, film_link])
#如果擷取的記錄數 >0 傳回真
if ret:
return True
else:
return False
def get_movie_links():
"""擷取清單頁影片資訊"""
# 1.定義清單的位址
film_list_url = "https://www.ygdy8.net/html/gndy/dyzz/list_23_1.html"
# 2.打開url位址,擷取資料 <http.client.HTTPResponse object at 0x0000026B03B4EC50>
response_list = urllib.request.urlopen(film_list_url)
# 2.1通過read()讀取網絡資源資料
response_list_data = response_list.read()
# 3.解碼擷取到的内容
response_list_text = response_list_data.decode("gb2312", "ignore")
# 4.使用正則得到所有的影片内容位址
#print(response_list_text) #輸出的就是右擊檢視網頁源代碼的内容
# 4.1 使用findall()根據正則查找所有影片對應的内容也位址
url_list = re.findall(r"<a href=\"(.*)\" class=\"ulink\">(.*)</a>", response_list_text)
# 4.2儲存位址
# [('/html/gndy/dyzz/20200809/60326.html', '2020年奇幻劇情《秘密花園》BD中英雙字幕'),
# ('/html/gndy/dyzz/20200809/60325.html', '2020年劇情傳記《放射性物質》BD中英雙字幕')..]
#print(url_list)# 以上用清單包起來,每一個是元組
# 定義一個字典,用于儲存影片資訊
films_dict = {}
# 計數
i = 1
# 循環周遊 url_list
for content_url, film_name in url_list:
# 電影的url位址是:https://www.ygdy8.net/html/gndy/dyzz/20200809/60326.html
content_url = "https://www.ygdy8.net" + content_url
# 打開内容頁位址
response_content = urllib.request.urlopen(content_url)
# 接收内容頁資料
response_content_data = response_content.read()
# 解碼得到内容頁的文本内容
response_content_text = response_content_data.decode("gb2312", "ignore")
#print(response_content_text)
# 取出下載下傳位址
result = re.search(r"bgcolor=\"#fdfddf\"><a href=\"(.*?)\">",response_content_text)
if result:
films_dict[film_name] = result.group(1)
print("已經擷取 %d 條資訊" % i)
i += 1
else:
break
return films_dict
def main():
film_dict = get_movie_links()
# 周遊字典
for film_name, film_link in film_dict.items():
# 如果資料庫中存在相同的資料就不再插入
if film_exist(film_name, film_link):
print("儲存失敗!影片:[%s]" % film_name)
continue
# 調用函數儲存資料
add_film(film_name, film_link)
if __name__ == '__main__':
# 建立連接配接對象
conn = connect(host="localhost", user="root", password="mysql", database="movie_db")
# 建立遊标對象
cur = conn.cursor()
# 調用爬出資料的函數
main()
# 送出資料
conn.commit()
# 關閉遊标
cur.close()
# 關閉連接配接
conn.close()
将爬取的電影資訊展示到網頁中
參考 實作簡單的web伺服器并傳回 固定資料 (頁面)給浏覽器
思路:把原本固定的内容變為從資料庫動态讀取
================将傳回的固定内容,變成從資料庫讀取的内容,拼接在一起,形成新的response_body===========================
import socket
import pymysql
def request_handler(new_client_socket, ip_port):
# 接收用戶端浏覽器發送的協定
request_data = new_client_socket.recv(1024)
# 判斷協定是否為空
if not request_data:
print("%s 用戶端已經下線!" % str(ip_port))
new_client_socket.close()
return
# 拼接響應封包
response_line = "HTTP/1.1 200 OK\r\n"
response_header = "Server:Python20WS/2.1\r\n"
response_header += "Content-type:text/html;charset=utf-8\r\n" # 防止亂碼,以html頁面顯示
response_blank = "\r\n"
response_body = " "
# 連接配接資料庫
conn = pymysql.connect(host="localhost", user="root", password="mysql",database="movie_db")
# 建立遊标
cur = conn.cursor()
sql = "select * from movie_link order by id desc"
cur.execute(sql)
result_list = cur.fetchall() #擷取執行結果的所有内容((1, 'film_name', 'film_link'),(... ).....)
# 周遊每一個元組
for row in result_list:
response_body += "%d.%s 下載下傳位址:[<a href='%s'>%s</a>] <br>" % (row[0], row[1], row[2], row[2])
# 關閉操作
cur.close()
conn.close()
response_data = response_line + response_header + response_blank + response_body
# 發送響應封包
new_client_socket.send(response_data.encode())
# 關閉套接字
new_client_socket.close()
def main():
# 建立套接字
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 設定位址重用
tcp_server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, True)
# 綁定端口
tcp_server_socket.bind(("", 8080))
# 設定監聽,讓套接字由主動變為被動
tcp_server_socket.listen(128)
while True:
# <socket.socket fd=500, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('10.1.26.31', 8080), raddr=('10.1.26.31', 64089)>
# ('10.1.26.31', 64089) 當有用戶端連接配接的話傳回新的socket對象,和ip位址,端口号
new_client_socket, ip_port = tcp_server_socket.accept()
print(" 新客戶來了:", ip_port)
# 接收資訊并作出響應
request_handler(new_client_socket, ip_port)
# 關閉套接字
tcp_server_socket.close()
if __name__ == '__main__':
main()