import hashlib
import requests,re
from lxml import etree
import pymongo
from queue import Queue
import threading
‘’’
使用第一种方法的弊端:
#1、线程的数量不可控。
#2、线程的执行顺序不可控。
控制线程的方法是使用队列
#使用线程类创建
#第一步:改成线程类
‘’’
#创建一个类 负责线程要做的事情
class Gushiwen(threading.Thread):
def init(self,q_type=None,crawl=None):
#经过初始化,入队操作,多线程的第一步开始了
#实现 传入队列和入队的名字,以及请求头
super().init()
self.q_type = q_type
self.name = crawl
self.ajax_headers = {
‘user-agent’: ‘Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36’,
‘cookie’: ‘login=flase; Hm_lvt_9007fab6814e892d3020a64454da5a55=1604653021,1604664055,1604711185; ASP.NET_SessionId=mi5gzudp2jtxammt0ke31pwd; wxopenid=defoaltid; Hm_lpvt_9007fab6814e892d3020a64454da5a55=1604711960’,
}
pass
def get_xpath(slef,url):
'''
发送请求,获取古诗文首页html
:param url:
:return:
'''
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36',
'cookie': 'login=flase; login=flase; wxopenid=defoaltid; Hm_lvt_9007fab6814e892d3020a64454da5a55=1604653021,1604664055,1604711185,1605251746; Hm_lpvt_9007fab6814e892d3020a64454da5a55=1605251748',
}
response = requests.get(url,headers=headers)
# print(response.text)
return etree.HTML(response.text)
def get_note_trans(self,id):
#(三) 获取诗文注释,
"""以五为纽扣总领各个方法,提取每条诗文的信息
通过ajax获取注释和解析
:param id:
:return:
"""
#https://so.gushiwen.cn/nocdn/ajaxfanyi.aspx?id=75B9117033181D93
ajax_url = 'https://so.gushiwen.cn/nocdn/ajaxfanyi.aspx?id={}'.format(id)
response = requests.get(ajax_url,headers=self.ajax_headers)
tree_part = etree.HTML(response.text)
#song_trans,song_note
song_trans = tree_part.xpath('string(//div[@class="contyishang"]/p[1])')
song_note = tree_part.xpath('string(//div[@class="contyishang"]/p[2])')
# print(song_trans,song_note)
return song_trans,song_note
def get_shanxi(self,id):
#(四)获取赏析
'''
获取赏析
:param id:
:return:
'''
ajax_url = 'https://so.gushiwen.cn/nocdn/ajaxshangxi.aspx?id={}'.format(id)
response = requests.get(ajax_url,headers=self.ajax_headers)
tree_part = etree.HTML(response.text)
shanxi = tree_part.xpath('string(//div[@class="contyishang"])')
# print(shanxi)
return shanxi
def write_to_mongo(self,item):
#(六) 五的方法调用我 写入mongo文件写的是作者 注释 赏析整个字典
'''
将数据写入mongo
:param item:
:return:
'''
#1、创建一个连接
client = pymongo.MongoClient()
#2、连接一个数据库
#client['数据库的名字']
db = client['古诗文']
#3、使用db数据库引用来操作集合:db['集合的名字']
db['songs'].update({'song_id':item['song_id']},{'$set':item},True)
print(item)
def get_md5(self,text):
#对古诗文的url哈希加密,为了变成数字更方便比较写入的主键,提高写入效率
return hashlib.md5(text.encode('utf-8')).hexdigest()
def parse_song(self,tree,url):
#(五) 对诗文页面信息的提取 其中有展开更多,调用方法获取
'''
提取诗歌信息
:param tree:
:return:
'''
song_infso = tree.xpath('//div[@class="cont"]//p[@class="source"]/a/text()')
# print(song_infso)
#作者
song_author = song_infso[1]
#朝代
song_dynasty = song_infso[0]
#诗歌名称
song_title = tree.xpath('//div[@class="cont"]//h1/text()')[0]
# print(song_title,url)
#诗歌内容--爬取新闻--string可以保持格式
song_content = tree.xpath('string(//div[@class="cont"]//div[@class="contson"])')
# print(song_content)
#注释和解析
zhushi_a_href = tree.xpath('//div[contains(@id,"fanyi")]/div/div[3]/a/@href')
if zhushi_a_href:
#有点击展开更多
# print(zhushi_a_href)
#提取id
#javascript:fanyiShow(677,'75B9117033181D93')
zhushi_id = re.search(r',\'(.*?)\'\)',zhushi_a_href[0]).group(1)
# print(zhushi_id)
song_trans,song_note = self.get_note_trans(zhushi_id)
else:
#没有点击展开更多
# print(zhushi_a_href)
#翻译
song_trans = tree.xpath('string(//div[@class="contyishang"]/p[1])')
#注释
song_note = tree.xpath('string(//div[@class="contyishang"]/p[2])')
#赏析
shanxi_a_href = tree.xpath('//div[contains(@id,"shangxi")]/div/div[3]/a/@href')
if shanxi_a_href:
#有展开更多
shanxi_id = re.search(r',\'(.*?)\'\)',shanxi_a_href[0]).group(1)
# print(shanxi_id)
shanxi = self.get_shanxi(shanxi_id)
# print(1)
else:
#无展开更多
# song_analyze = tree.xpath('string(//div[@class="contyishang"]/p[2])')
print(song_title, url)
shanxi = tree.xpath('//div[@class="contyishang"]/p[1]/text()')[-1]
# print(shanxi)
item = {}
item['song_author'] = song_author
item['song_dynasty'] = song_dynasty
item['song_title'] = song_title
item['song_content'] = song_content
item['song_trans'] = song_trans
item['song_note'] = song_note
item['shanxi'] = shanxi
item['song_id'] = self.get_md5(url)
print(item)
self.write_to_mongo(item)
(一)、
def get_song_types(self):
# 1、获取分类列表
base_url = 'https://so.gushiwen.cn/shiwen/'
#获得诗文网页html
tree_index = self.get_xpath(base_url)
# 获取诗歌分类
song_types = tree_index.xpath('//div[@class="cont"]/a/@href')
return song_types
def parse_song_type(self,song_type):
#(八) 传入的是诗文类别
'''
解析是个分类
:param song_type:
:return:
'''
# print(song_type)
full_type_url = 'https://so.gushiwen.cn' + song_type
tree_type = self.get_xpath(full_type_url)
song_urls = tree_type.xpath('//div[@class="typecont"]/span/a/@href')
# print(song_urls)
# 3、进入诗歌详情页,获取诗歌信息
for song_url in song_urls:
if 'https:' not in song_url:
full_song_url = 'https://so.gushiwen.cn' + song_url
else:
full_song_url = song_url
tree_song = self.get_xpath(full_song_url)
# 4、提取数据
self.parse_song(tree_song, full_song_url)
# def main(self):
# #1、获取分类列表
# base_url ='https://so.gushiwen.cn/shiwen/'
# tree_index = self.get_xpath(base_url)
# #获取诗歌分类
# song_types = tree_index.xpath('//div[@class="cont"]/a/@href')
# #2、循环遍历诗歌分类,进入分类页面,获取该分类下的所有诗歌详情页url
# for song_type in song_types:
# # print(song_type)
# full_type_url = 'https://so.gushiwen.cn' + song_type
# tree_type = self.get_xpath(full_type_url)
# song_urls = tree_type.xpath('//div[@class="typecont"]/span/a/@href')
# # print(song_urls)
# # 3、进入诗歌详情页,获取诗歌信息
# for song_url in song_urls:
# if 'https:' not in song_url:
# full_song_url = 'https://so.gushiwen.cn' + song_url
# else:
# full_song_url = song_url
# tree_song = self.get_xpath(full_song_url)
# # 4、提取数据
# self.parse_song(tree_song, full_song_url)
def run(self):
#(七) 任务队列的执行调用
while True:#一个线程重复不断做多个任务
if self.q_type.empty():#任务队列为空,线程就停止工作
break
#线程正常工作的步骤
#1、从任务队列获取一个任务
#get出队 q_type=诗文的类
song_type = self.q_type.get()
print(f'@{self.name}开始爬取{song_type}')
self.parse_song_type(song_type)
if name == ‘main’:
“”"“将诗文类型定位为循环条件,开启多线程,线程定为四个并取名”""
g = Gushiwen()
#开启多线程
#1、创建一个任务队列
#明确任务:任务就是顶层循环条件–诗歌类型
q_type = Queue()
# (二) 将诗文类别入队 2、初始化任务队列
song_types = g.get_song_types()
for song_type in song_types:
#put:队列的入队
q_type.put(song_type)
#3、创建一个list, # 这个list的长度就是线程的数量, list里面的内容就是线程的名字
crawl_list = [‘aa’,‘bb’,‘cc’,‘dd’]
#4、循环这个list,开启多线程
for crawl in crawl_list:
#创建线程
t = Gushiwen(q_type,crawl)
#启动线程
t.start()