目录
一、前言:
本代码参考网上现有爬取猫眼排行榜代码进行优化及修改并新增写入doc,内容排序,图片爬取等功能,因参考链接后来没有找到,在这里对原作者说声抱歉。
环境为python3,编译器使用pycharm。
话不多说,直接帖代码。
二、源码:
import time
import json
import requests
import socket
from pyquery import PyQuery
from multiprocessing import Pool
from requests.exceptions import RequestException
from urllib.request import urlretrieve
from src.File import File
from retrying import retry
from docx import Document
from docx.shared import Inches
def get_one_page(url):
# 获取一个页面
headers = {"User-Agent": "Mozilla/5.0"}
try:
response = requests.get(url, headers=headers)
if response.status_code != 200:
return None
except RequestException:
return None
return response.text
@retry(stop_max_attempt_number=4)
def save_img(img, name):
for img_info in img("img").items():
img_test = PyQuery(img_info)
try:
if img_test("img").attr("data-src") is not None:
url = img_test("img").attr("data-src")
img_name = "picture/" + name + ".png"
if file.isFileExit(img_name) is False:
urlretrieve(url, img_name)
except:
raise Exception(name + " picture is not install")
def pase_one_page(text):
# 解析页面内容
doc = PyQuery(text)
for info in doc("dl.board-wrapper dd").items():
dct = {}
dct["index"] = info.find(".board-index").text()
dct["name"] = info.find("p.name a").text()
dct["star"] = info.find("p.star").text()
dct["releasetime"] = info.find("p.releasetime").text()
dct["score"] = info.find(".score").text()
img = PyQuery(info)
try:
save_img(img, dct["name"])
except:
print(dct["name"] + " picture is not install")
yield dct
def write_to_file(content):
# 写入文件
with open("movie.txt", "a", encoding="utf-8") as f:
f.write(json.dumps(content, ensure_ascii=False) + "\n")
def main(offset):
# 程序入口
url = "http://maoyan.com/board/4?offset={offset}"
text = get_one_page(url.format(offset=offset))
for item in pase_one_page(text):
write_to_file(item)
def compare(every):
j = json.loads(every)
return int(j["index"])
def sort_file():
lines = []
f = open("movie.txt", "r")
for line in f:
lines.append(line)
f.close()
if len(lines) is not 0:
lines.sort(key=compare)
f = open("movie.txt", "w+")
for line in lines:
f.write(line)
f.close()
def write_to_doc():
doc = Document()
lines = []
f = open("movie.txt", "r")
for line in f:
lines.append(line)
j = json.loads(line)
img = "picture/" + str(j["name"]) + ".png"
doc.add_paragraph(j["index"] + "、" + j["name"])
doc.add_picture(img, width=Inches(2))
doc.add_paragraph(j["star"])
doc.add_paragraph(j["releasetime"])
doc.add_paragraph("评分: " + str(j["score"]))
doc.add_paragraph("-" * 118)
doc.save("movie.doc")
def init():
socket.setdefaulttimeout(2)
file.remove_file("picture")
with open("movie.txt", "r+") as f:
f.truncate()
if __name__ == "__main__":
start = time.time()
file = File()
if file.isDir("picture") is False:
file.mkdir("picture")
init()
pool = Pool(processes=10)
pool.map(main, [i * 10 for i in range(10)])
pool.close()
sort_file()
write_to_doc()
end = time.time()
print("time is " + str(end - start) + "s")
三、爬取结果doc文本展示:
四、关键库:
1、pyquery
解析html库,用于将请求到的html网站源码进行逐段标签的解析,提取有用的榜单信息,过滤不需要的信息。
2、multiprocessing
线程池,用于开启线程池,对线程进行管理,对资源进行方便管理。
3、requests
用于实现http请求,该库封装了很多方便的api,简化http的写法。
4、urllib3
主要使用了urlretrieve,用于请求服务器的图片资源,并保存到本地。
5、retrying
python提供的重试包,通过注解的方式来对某一可能运行失败的程序段进行重试功能。
6、docx
python提供的用于操作doc文档的库,利用该库可以方便的将文字和图片按照一定的顺序方式写入doc文档中。
五、主要的优化点:
1、多线程的优化,排榜为100部电影,分为10个线程去爬取,单个线程处理10部电影,限制线程池数量为10,经过测试在这个时候运行速度最快。
2、爬取内容的优化,增加对电影海报图片的爬取,并利用urlretrieve进行下载保存到本地,因该部分需要请求服务器图片资源,可能导致失败,利用retrying模块对该部分功能进行重试4次的操作,保证图片下载完全。
3、保存文件txt的优化,txt不支持对于图片的保存,且多线程下对文件进行操作时会导致电影排榜的乱序,所以经过思考,该用doc格式来进行结果的保存,并对爬好的数据进行重新遍历排序,并对doc文本中的内容进行美化。
4、对文件目录进行结构优化,并在每次执行前对文本进行清空操作,避免影响下一次的结果保存。
5、对爬取速度进行优化,经过实验,发现在增加图片的爬取功能后,爬虫运行速度会大幅降低,原因在于访问图片资源的失败时过于耗时,所以对requests进行了进一步的了解,发现该库的底层实现依旧采用的是socket,所以通过对socket的默认超时时间设置为2s,来加快整个爬取的速度。同时当图片文件存在时,跳过对该图片的资源请求,以节省时间。
六、最终耗时
七、写到最后
1、此代码中有引入我其它的封装好的一些文件操作类,可能无法直接使用,这些都是比较基础的文件操作,想必修改起来并不困难。
2、这份代码并非没有优化点,还是有很多值得优化思考的地方,包括界面的加入以及定时爬取的功能,常驻工具的设置,路径的变化等等,如何进一步优化完善就各凭本事吧,我也只是做了部分。
3、有个缺陷在于如果爬取到的图片数量依然不足100,在写入doc文本时会有异常,这个加一个计数判断就好,我这里就懒得弄了,直接retrying次数增加也是可以的。