文章目录
- 多线程实现简单的爬虫保存在字典中(电影天堂)
- 爬取电影信息保存在数据库中
- 将爬取的电影信息展示到网页中
多线程实现简单的爬虫保存在字典中(电影天堂)
=============================使用多线程得到下载的电影地址(没有使用的时候,很慢)==================================
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()