# 通过官网单页拿到文章标题和发布时间,在通过抓包拿到浏览人数的数据
# 抓取所有页所有文章数据
from concurrent.futures import ThreadPoolExecutor
import requests
from lxml import etree
import csv
all_list = [] # 保存所有文章的信息
# 保存为csv格式
f = open("hunan.csv", mode="w", encoding="utf-8", newline="")
writer = csv.writer(f)
def main():
baseurl = "http://www.hnuit.edu.cn/p1/mtsj.html"
# 单独拿第一页吧,从第二页开始好一点
get_data(baseurl)
# 单纯死循环(链接会超时,可能是http链接占用过多只拿大页面也要请求20次)
# for i in range(2, 20):
# url_resp_list = get_data(f"http://www.hnuit.edu.cn/p1/mtsj_{i}.html")
# print(url_resp_list)
# 用多线程提高效率(可以解决http链接占用过多情况,缺点就是无顺序的)
"""用多线程view坑定不能按顺序拿到了,在一个循环里连续访问15个url拿到15个view,湖南信息学院不可能,如果用多线程去接这个任务,
可以快速拿到15个view但是对不上号,你不知道那个view是那个的,可以试试开两个多线程"""
# 要是单线程的话,一个线程要跑(19个主页面*每一个主页面要访问15次url拿到所有文章id)=285次
with ThreadPoolExecutor(50) as t2:
for i in range(2, 20):
t2.submit(get_data, f"http://www.hnuit.edu.cn/p1/mtsj_{i}.html")
# 看一下牛逼的湖南信息学院能拿多少信息
print(f"此次拿到的数据为{len(all_list)}条")
# 保存为csv格式
save_data(all_list, len(all_list))
def save_data(save_list, num):
print("sava...")
# 注意是左闭右开
for i in range(0, num): # 每次拿多少我就存多少,这样就不会越界了,因为不可能每次都拿到135条,有一些页面可能404也不一定
writer.writerow(save_list[i])
print(f"第{i}条!")
print("保存成功!")
def get_data(url):
resp_page = ask_url(url)
# 创建一个etree对象,并给定页面
tree = etree.HTML(resp_page)
# 用xpath()查找大致范围,拿到包含所有文章的标签信息
li_list = tree.xpath("/html/body/div/div[2]/div[2]/ul[1]/li")
# print(li_list) 查找到15个文章节点
# 在每一篇文章里面拿详情链接后直接访问保存到列表
"""把这个循环改成多线程能不能解决超时问题?15个线程完成任务以后就返回一页中的所有文章信息"""
with ThreadPoolExecutor(15) as t1:
for li in li_list:
t1.submit(base_date, li)
return all_list
def base_date(li):
"""一页中的一个小文章的数据提取任务,是为了迎合多线程准备的"""
list = [] # 保存一篇文章的信息
time = li.xpath("./p[1]/text()")[0]
title = li.xpath("./p[2]/a/text()")[0]
id = li.xpath("./p[2]/a/@href")[0]
# 把文章的id拿出来,不太会用分隔符截取,只能用切片
id = id[18:23]
# 通过访问链接拿观看人数
view = get_view(id)
list.append(title)
list.append(view)
list.append(time)
all_list.append(list)
def get_view(id):
"""详情链接请求时没几个就超时了
1.ip被封。(能请求就说明ip没被封)
2.http的连接数超过最大限制。headers的Connection参数默认为keep-alive,导致之前所有的链接都一直存在,占用了后续的链接请求。
3.网站服务器太差,爬取请求的速度太快。(它急着响应,而网站的服务器不给力,就直接断掉了)
解决方案:
1.换ip,更换headers或者使用代理ip。
2.将Connection参数的值改为close。或者在requests.get前一行添加requests.adapters.DEFAULT_RETRIES = 5,意思是连接失败时重连五次。
3.因为单线程请求过多url(单线程请求的url很一直占用)导致http的链接数量超过了最大限制(也可能是对方服务器不好)
那么用多线程来解决就很好,多开几个线程,那么每个线程请求的url数量就会减少,很大程度上降低压力"""
view_url = f"http://www.hnuit.edu.cn/content/view?contentId={id}"
view_url_resp = requests.get(view_url) # 因为那个方法返回的是text格式
dic = view_url_resp.json()
view = dic['data']['views']
return view
# 拿到url的源代码
def ask_url(url):
resp = requests.get(url)
resp.encoding = "utf-8"
# print(resp.text) # 拿到了访问的页面
return resp.text
if __name__ == "__main__":
main()
print("over!")