极其罕见的与人谈崩,有点难受,不知道以后怎么再去面对对方。

连着肝了两晚上,今晚基本赶完,写好说明文档,也不知再说些什么,如果有办法能抹去以前的一切就好了。

这个东西算我欠你的,事已至此,我亦无力纠缠,这一个多月来就像一场梦。

目之所及,心之所向,情之所往,皆为幻象。

我也该醒醒认清自己了。

RAEDME.md

# NewsCrawl# 1 简介
1. 这是一个基于新闻网站的新闻文本爬虫, 向指定微信群中定时发布新闻消息的项目;
2. 20191226商议时标注了四个网站, 但是发现第一电动网新闻质量参差不齐(我怀疑是不是个卖车平台), 所以没有去做, 目前只进行了编写三个网站的爬虫, 分别是:- SEMI大半导体产业网: http://www.semi.org.cn/- 新材料在线: http://www.XCL.com/- 高工锂电: https://www.gg-lb.com/# 2 使用说明## 2.1 第一次使用前
1. 根目录下应当存在的文件: - ../               根目录+ Crawl.py        爬虫总父类+ Crawl_SEMI.py   大半导体产业网爬虫类+ Crawl_XCL.py    新材料网爬虫类+ Crawl_GGLB.py   高工锂电爬虫类+ utils.py        常用工具函数+ manage.py       主函数+ meta/           (文件夹)存放元数据- websites.csv  存放网站信息的csv文件- keywords.txt  存放关键词的txt文件+ README.md       (可选)即本文件2. 本爬虫全部基于requests库, 没有使用selenium的方法, 如果是安装的conda, 则额外安装一个itchat库即可(该库为微信操作库)- 版本: python 3.x (我的是python 3.6)- 所有用到的库:+ requests+ os + re+ time+ pandas+ itchat: 该库conda没有预装, 需手动安装: pip install itchat即可+ datetime+ bs43. 以下将分别对各个文件的结构做简要说明;## 2.2 文件说明
1. websites.csv: csv文件, 存放需要爬取的网站(目前只放三个网站), 各条目间的间隔应为"\t", 即制表符Tab: - 表头为: "index   site    url";+ index: 1,2,3,...+ site: 网站名称+ url: 网站URL, 注意一定要是根域名URL, 如http://www.semi.org.cn/, 而非http://www.semi.org.cn/index, 虽然它们指向同一页面;- 如下所示:'''index  site    url 1   SEMI大半导体产业网 http://www.semi.org.cn/2    新材料在线   http://www.XCL.com/3    高工锂电    https://www.gg-lb.com/'''2. keywords.txt: 一行写一个关键词, 如下所示:'''永磁半导体靶材石英氮化镓砷化镓液晶显示面板正极负极隔膜电解液高温合金碳纤维钛材'''3. Crawl.py  爬虫总父类- 编写了Crawl类, 该类仅有一个构造函数__init__();- 在Crawl类的构造函数中定义了各个网站爬虫中常用的变量: + 类构造变量(即实例化类时应当传入的参数)- self.user_agent: 用户代理(即伪装浏览器头);+ 类常用变量:- self.wordspace: 当前工作目录- self.date: 类创建时间(如20191228)- self.dirs: 存放了一些需要新建的文件夹, 如news(新闻文件), log(记录文件), temp(临时文件)- 预先编译了一些可能用到的正则表达式: self.invalid_compiler, self.label_compiler, self.date_compiler + 类初始化: 生成一些其他类变量及一些初始化操作- 检查根目录下是否存在meta文件夹(如不存在则抛出异常);- 新建news, log, temp三个文件夹:+ 每个文件夹中新建以当日日期命名的文件夹; + 在news文件夹中还会在当日的文件夹里为每个网站新建一个文件夹;- 以上新建操作都是在文件夹不存在的情况下才会发生, 如文件夹存在则自动跳过;- 运行后根目录下应当多出如下文件(以2019年12月28日为例):+ ../- log/                                    文件夹: 存放记录文件+ 20191228/                             文件夹: 存放20191228的记录文件(目前代码没有生成过记录文件)- temp/                                   文件夹: 存放临时文件+ 20191228/                             文件夹: 存放20191228的记录文件(目前代码没有生成过记录文件)- news/                                   文件夹: 存放新闻文件+ 20191228/                             文件夹: 存放20191228爬取到的新闻文件- 1_SEMI大半导体产业网/               文件夹: 存放大半导体产业网20191228爬取到的新闻数据- 2_新材料在线/                       文件夹: 存放新材料网20191228爬取到的新闻数据- 3_高工锂电/                            文件夹: 存放高工锂电网20191228爬取到的新闻数据- 创建一些其他类变量:+ self.urls: <列表>存放meta/websites.csv中的网址;+ self.keywords: <列表>存放meta/keywords.txt中的关键词;+ self.directorys: <列表>存放文件夹信息, 如['1_SEMI大半导体产业网', '2_新材料在线', '3_高工锂电'];- 使用方法: + c = Crawl()  - 注意可以写成 c = Crawl(user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0")- user_agent我已经赋给默认值, 所以不传该参数也可以;+ 效果: 生成各个文件夹;4. Crawl_SEMI.py, Crawl_XCL.py, Crawl_GGLB.py 三个网站的爬虫类- 分别封装了Crawl类的三个子类: SEMI类, XCL类, GGLB类;- 这三个类的结构类似, 我一并说明:+ 构造函数: __init__(day_before=0)  - 注意SEMI类比其他两个要多一个参数plates, 默认值为["半导体新闻"], 因为SEMI网站上首页有很多板块, 目前只写了"半导体新闻"板块的, 其他与"半导体新闻"结构类似的板块可以加在这个列表中作为构造参数传入;- day_before指获取多少天前的新闻, 如果是0则默认只获取今天的;- 构造函数里先继承父类的构造函数(即生成很多类变量以及检查文件夹生成情况, 缺的文件夹会新建), 然后做了一些其他的常规操作;+ 爬取新闻列表的函数: parse_newslist(session,url)- 获取新闻列表面上的数据: 如该页面上所有的新闻标题, 新闻发布时间, 每个新闻的链接URL, 以及可能有新闻来源;+ 爬取新闻内容的函数: parse_newcontent(session,url)- parse_newslist(session,url)函数得到的新闻链接URL, 对每个URL中的新闻内容获取, 一般可以获取到新闻的文本, 新闻的来源等;+ 主函数: main()- 利用以上两个函数获取到所有新闻数据, 存放在一个列表中, 每个新闻有5个属性(url,title,date,paragraphs,source), 结构如下:+ [{"url": "http://www.semi.org.cn/news/news_show.aspx?ID=58264&classid=117","title": "耐威科技北京8英寸MEMS国际代工线首台设备搬入仪式圆满举行","date": "20191227","paragraphs": ["段落1","段落2",..."段落n",]"source": "出自:耐威科技",},{"url": "http://www.semi.org.cn/news/news_show.aspx?ID=58264&classid=117","title": "耐威科技北京8英寸MEMS国际代工线首台设备搬入仪式圆满举行","date": "20191227","paragraphs": ["段落1","段落2",..."段落n",]"source": "出自:耐威科技",             },...{"url": "http://www.semi.org.cn/news/news_show.aspx?ID=58264&classid=117","title": "耐威科技北京8英寸MEMS国际代工线首台设备搬入仪式圆满举行","date": "20191227","paragraphs": ["段落1","段落2",..."段落n",]"source": "出自:耐威科技",              }]+ 然后将该列表以字符串格式写入到指定文件夹中, 如news/20191228/1_SEMI大半导体产业网/SEMI_20191228.txt中, 便于后续处理5. utils.py  一些常用的工具函数- sent_tokenize(x)  中文分句- generate_forward_content(news_info,                                       # news_info参数即为上文中每条新闻的字典({url: ..., title: ..., date: ..., paragraphs: ..., source: ...,})sent_minlen=20,                                                         # 每句话的最少字符数(用于过滤废话和无关信息)minlen=300,                                                            # 推送文字的最小长度(只要少于这个字数就会再加一句话)maxlen=500,                                                            # 推送文字的最大长度(如果大于这个字数就不增加句子了)keywords=None,                                                        )  该函数返回可以直接发送到微信群中的字符串;- send_group(message,gname)  发送message到群名为gname的群中+ 注意一下, 如果gname没有找到会抛出异常, 最好的方法是使用前先在群里发一条信息, 然后就可以找到了, 否则很多微信群是找不到的;6. manage.py 主函数- 详见该文件, 一些参数都用大写字母命名的变量做好了;## 2.3 使用方法及可能的问题
1. 根目录下打开cmd: python manage.py 即可
2. 三个爬虫很类似, 基本上不会有太大问题, 我将一些要点写在这里:- SEMI网站的新闻较少, 一天也就几条, 所以只拿了第一页的新闻, 后面几页就不管了, 因为三个爬虫中它响应最慢;+ SEMI的新闻文本相对问题少, 有明确的文本区域, 各段落文本也应该都是在<p>标签下的;- 新材料网一天会有大约3~4页的新闻, 相对多一些, 它的问题在于各段落新闻文本并不都是在<p>标签中, 有可能有<br>半标签, + 如http://www.xincailiao.com/news/news_detail.aspx?id=559043新闻文本都是在<p>标签下的;+ 如http://www.xincailiao.com/news/news_detail.aspx?id=559055则夹杂很多<br>半标签;+ 因此新材料网站我直接对新闻文本区域用去标签处理, 应该也问题不大- 高工锂电网不存在明确的新闻文本区域, 因此就找了所有的<p>标签, 问题也应该不大;- SEMI拿的是半导体新闻板块下的, 新材料网与高工锂电都是拿的最新资讯(而非约定的半导体订阅号之类的, 因为发现那些订阅号更新的很少)3. 关于关键词的问题:- 如何查看文本各个关键词权重?'''# 安装jieba库: pip install jieba 该库较大需要较长时间, 我不清楚conda有没有预装, 应该是没有from jieba import analysea = analyse.extract_tags("这是一个文本xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",topK=20,withWeight=True,allowPOS=('n',"nr","ns"))print(a) # 则输出a可以看到前20个关键词的权重, 我试过测试关键词, 但是发现基本上都对应不上excel中的关键词'''- 如何解决该问题: 可以考虑计算关键词词向量的相似度, 设计一个简单的近似算法来评估新闻文本匹配excel中所有关键词的一个得分, 但这有点麻烦, 一方面词向量文件很大, 读取和使用都很耗时, 另外我觉得我选的这几个新闻类别与这些关键词大部分还是匹配得上的, 综上所以就不使用4. 关于log和temp文件夹: - log文件夹: 一般可以放一些异常抛出的报告, 或者爬虫的一个精确时间记录, 虽然我都没有写;- temp文件夹: 放一些临时文件, 可能也不会有什么临时文件, 万一会有呢?- 总之这两个文件夹目前代码里没有放东西在里面;5. 关于定时启动:- 这个说实话很麻烦, 如果是用服务器, 我不清楚怎么登录微信, 因为在电脑上需要扫码登录微信, 如果用的是电脑, 我不清楚itchat的登录会持续多久, 可能需要每天手动运行然后登录, 如果itchat可以做到长时间保持微信号登录状态, 则可以定时使用(定时使用的代码在manage.py中被注释掉的一长串中)

Crawl.py

# -*- coding: UTF-8 -*-
# Author: 囚生
# 定义一个爬虫父类, 被其他各个网站的爬虫子类继承, 用于对各个爬虫中出现的共性问题做处理: 类项目下各个文件夹, 文件的初始化, 元数据的读取, 共用参数的设置等;import os
import re
import time
import pandasclass Crawl():def __init__(self,user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0",):                                                                     # 类构造函数""" 类构造参数 """self.user_agent = user_agent                                     # 用户代理""" 类常用参数 """self.workspace = os.getcwd()                                  # 类工作目录self.date = time.strftime("%Y%m%d")                               # 类创建时间self.dirs = {                                                   # 常用的文件夹"元": "meta",                                                # 存放元文件的文件夹: 该文件夹必须在第一次使用时预先设置, 里面的文件请根据README配置好"新闻": "news",                                              # 存放新闻文件的文件夹"记录": "log",                                                # 存放记录文件的文件夹"临时": "temp",                                               # 存放临时文件的文件夹}# 一些常用的正则表达式self.invalid_pattern = r'[/|\\|?|*|\||"|<|>|:| |\r|\n|\t]'       # 文件名中不合理的字符正则self.invalid_compiler = re.compile(self.invalid_pattern)         # 编译正则self.label_pattern = r'<[^>]+>'                                  # HTML中标签的正则: 去除HTML中的标签, 剩下的就是需要的信息self.label_compiler = re.compile(self.label_pattern)           # 编译正则self.date_regular = r"[^\d]"                                   # 用于去除日期中的非数字的正则: 去除日期中非数字字符, 这样日期就变成了yyyymmddself.date_compiler = re.compile(self.date_regular)               # 编译正则""" 类初始化 """for key,value in self.dirs.items():                                 # 确认self.dirs中的目录是否都存在if not os.path.exists(value):                                 # 不存在则新建或抛出异常if key=="元": raise Exception("找不到元文件夹!请阅读README!")    else:loginfo = "正在新建{}文件夹...".format(key)print(loginfo)                                       # 输出日志信息os.mkdir("{}/{}".format(self.workspace,value))os.mkdir("{}/{}/{}".format(self.workspace,value,self.date))else:                                                      # 存在则进一步检查是否存在当日的文件夹if not os.path.exists("{}/{}".format(value,self.date)) and not key=="元":loginfo = "正在{}文件夹中新建{}文件夹...".format(key,self.date)print(loginfo)                                         # 输出日志信息os.mkdir("{}/{}/{}".format(self.workspace,value,self.date))self.websites = pandas.read_table("{}/websites.csv".format(self.dirs["元"]),header=0,sep="\t",dtype=str,encoding="UTF-8")with open("{}/keywords.txt".format(self.dirs["元"]),"r",encoding="UTF-8") as f:self.keywords = list(map(lambda x: x.strip(),f.read().splitlines()))self.urls = self.websites["url"].tolist()                      # 所有的网址self.directorys = []                                            # 对应self.urls的存放各个网址新闻的文件夹名for i in range(self.websites.shape[0]):site = self.websites.loc[i,"site"]directory = "{}_{}".format(i+1,site)                         # 命名文件夹self.directorys.append(directory)if not os.path.exists("{}/{}/{}".format(self.dirs["新闻"],self.date,directory)):loginfo = '正在为"{}"创建{}的文件夹...'.format(site,self.date)print(loginfo)                                          # 输出日志信息os.mkdir("{}/{}/{}/{}".format(self.workspace,self.dirs["新闻"],self.date,directory))if __name__ == "__main__":crawl = Crawl()

Crawl_SEMI.py

# -*- coding: UTF-8 -*-
# Author: 囚生
# 获取SEMI大半导体产业网站(半导体新闻板块的内容)from Crawl import Crawlimport re
from requests import Session
from bs4 import BeautifulSoup
from datetime import datetimeclass SEMI(Crawl):def __init__(self,plates=["半导体新闻"],                                            # 获取哪些板块的新闻: 默认获取半导体新闻day_before=0,                                                    # 获取几天前的新闻: 默认0即获取今天的数据):  Crawl.__init__(self,)                                            # 继承父类Crawl的成员变量及方法""" 类构造参数 """self.plates = plates                                             # 获取哪些板块的新闻self.day_before = day_before                                    # 获取几天前的新闻""" 类常用参数 """self.homeURL = self.urls[0].strip("/")                          # SEMI主页URL: 它应该位于websites.csv列表中的第1个self.session = Session()                                      # 类会话对象""" 类初始化 """self.session.headers = {"User-Agent": self.user_agent}          # 为类会话设置用户代理def parse_newslist(self,session,url):                               # 获取新闻列表: session(类会话对象), url(新闻列表的网址URL)html = session.get(url).text                                  # 获取新闻列表页面的HTMLsoup = BeautifulSoup(html,"lxml")                                 # 解析HTMLaLabels = soup.find_all("a")                                     # 获取所有的a标签news = list()                                                    # 用于存储列表中各个新闻的(新闻标题,新闻超链接URL,发布时间)for a in aLabels:                                                 # 遍历所有的a标签href = a.attrs.get("href")                                     # 获取新闻超链接URLif href is None: continue                                   # 如果没有超链接则跳过: 确实存在一些<a>标签没有超链接if "news_show.aspx" in href:                              # 判断是否为新闻链接: 这里以超链接URL中是否包含"news_show.aspx"子串为标准href = href.strip("/")                                     # 去除超链接URL两端的"/"字符title = str(a.string)                                  # 获取新闻标题date = a.find_parent("td").find_next_sibling("td").string# 这里使用了很硬的寻找日期的方式: 即超链接的父亲的下一个兄弟, 也可以用<td align="right">属性来定位, 但也很硬;date = str(date)                                        # 转化为字符串date = self.date_compiler.sub("",date)                   # 删除date中不为数字的字符(即希望得到的日期格式是yyyymmdd, 便于转化为日期数据格式比较)""" 这片代码用于检查新闻日期是否符合day_before要求, 不合要求则退出 """datetime_date = datetime.strptime(date,"%Y%m%d")     datetime_now = datetime.strptime(self.date,"%Y%m%d")interval = datetime_now - datetime_dateif interval.days>self.day_before: break                    # 退出newsURL = "{}/{}".format(                                # 事实上href并不是新闻页面的绝对URL, 是基于新闻列表的相对URL, 因此需要通过新闻列表页面网址的URL来生成绝对地址"/".join(url.split("/")[:-1]),                      # 前半部分是新闻列表页面URL的去除最后一个"/"之后的部分, 剩下的字符串href.strip("/")                                      # 后半部分即为<a>标签中的超链接URL(去除两端的"/"字符))news.append({                                             # 将(新闻链接URL,新闻标题,新闻发布时间)作为三元组添加到news列表中"url": newsURL,"title": title,"date": date,})return news                                                       # 返回第一页上的所有新闻信息(第一页的应该基本上够用了)def parse_newscontent(self,session,url):                           # 获取新闻内容: session(新闻)html = session.get(url).text                                  # 获取新闻内容页面的HTMLsoup = BeautifulSoup(html,"lxml")                                 # 解析HTMLtable = soup.find("table",class_="gongzuo")                       # 新闻的主体内容在某个<table>标签中span1 = soup.find("span",attrs={"id":"lblAuthor"})              # 文章来源在一个<span>标签中span2 = soup.find("span",attrs={"id":"lblTitle"})               # 文章标题在一个<span>标签中if table is None: ps = soup.find_all("p")                        # 为了防止网站页面结构发生更新或者有例外情况: 那就直接找所有的<p>标签else: ps = table.find_all("p")                                   # 有这张<table>则从这个<table>中寻找所有的<p>标签if span1 is not None: source = str(span1.string)               # 找到这个<span>标签则获取来源else: source = "出自: SEMI大半导体产业网"                            # 否则默认来源是SEMI网if span2 is not None: title = str(span2.string)                  # 如果能找到文章标题则获取else: title = None                                               # 否则就置None, 返回None表明不修改文章标题paragraphs = list()                                                 # 用来存储各个段落for p in ps:                                                  # 遍历每个段落content = self.label_compiler.sub("",str(p))                 # 去除标签paragraphs.append(content)                                    # 非空段落添加到paragraphs中return paragraphs,source,title                                  # 返回新闻段落列表与来源def main(self,):html = self.session.get(self.homeURL).text                        # 首先访问主页并获取HTMLsoup = BeautifulSoup(html,"lxml")                                 # 解析页面aLabels = soup.find_all("a")                                   # 找到所有<a>标签hrefs = list()                                                    # 用于存储需要爬取的板块的"更多>>"链接的URLfor a in aLabels:                                                 # 遍历所有<a>标签: 为了找到"更多>>"string = str(a.string)                                        # 获取<a>标签的字符串if string=="更多>>":                                             # 如果该<a>标签的字符是"更多>>"则有可能是指向新闻列表try: plate = str(a.find_parent("td").find_previous_sibling("td").string)except: continue                                      # 有可能压根找不到这些条件if plate in self.plates: hrefs.append("{}/{}".format(   # 如果该板块是需要的, 则将该板块"更多>>"链接的URL添加到hrefs: 注意href的超链接是相对地址, 因此需要添加根域名http://www.semi.org.cnself.homeURL,a.attrs["href"].strip("/"),))print("共有{}个板块".format(len(hrefs)))for i in range(len(hrefs)):                                      # 遍历所有的新闻列表页面URL: 默认只获取"半导体新闻"则hrefs中只有1个URLprint("正在获取第{}个板块: {}".format(i+1,self.plates[i]))news = self.parse_newslist(self.session,hrefs[i])           # 获取新闻列表页面的新闻信息: print("  - 该板块下有{}条新闻".format(len(news)))for j in range(len(news)):                                  # 遍历列表中所有新闻的基本信息: 链接URL, 标题, 发布时间print("正在获取第{}条新闻...".format(j+1))result = self.parse_newscontent(self.session,news[j]["url"])"""根据result信息, 更新news中每个字典, 如此字典有五个键值对,分别是新闻链接URL,新闻标题,新闻发布日期,新闻内容(段落列表),新闻来源"""news[j]["paragraphs"] = result[0]                         # 更新新闻内容news[j]["source"] = result[1]                            # 更新新闻来源if result[2] is not None: news[j]["title"] = result[2]   # 更新新闻标题: 因为直接从新闻列表中拿的新闻标题有的太长就会有省略号...with open("{}/{}/{}/SEMI_{}.txt".format(self.dirs["新闻"],self.date,self.directorys[0],self.date),"w",encoding="UTF-8") as f:f.write(str(news))return newsif __name__ == "__main__":semi = SEMI(day_before=1)semi.main()

Crawl_XCL.py

# -*- coding: UTF-8 -*-
# Author: 囚生
# 获取新材料网新闻(最新新闻板块的内容)from Crawl import Crawlimport re
from requests import Session
from bs4 import BeautifulSoup
from datetime import datetimeclass XCL(Crawl):def __init__(self,day_before=0,                                                   # 获取几天前的新闻: 默认0即获取今天的数据):  Crawl.__init__(self,)                                            # 继承父类Crawl的成员变量及方法""" 类构造参数 """self.day_before = day_before                                     # 获取几天前的新闻""" 类常用参数 """self.homeURL = self.urls[1].strip("/")                          # 新材料网主页URL: 它应该位于websites.csv列表中的第2个# 最新新闻URLself.newestURL = self.homeURL+"/news/news_list.aspx?fenlei=latest&page={}"self.session = Session()                                         # 类会话对象""" 类初始化 """self.session.headers = {"User-Agent": self.user_agent}          # 为类会话设置用户代理def parse_newslist(self,session,url):                               # 获取新闻列表: session(类会话对象), url(新闻列表的网址URL)html = session.get(url).textsoup = BeautifulSoup(html,"lxml")"""通过分析网页发现新闻列表区域在一个<div>标签中,但这个div标签的class属性太长, 如果某天发生改变就会很糟糕,我们选择先定位新闻列表底部的1,2,3,4翻页按钮, 因为它的class为page是很有特征的, 并且预计不会发生较大改变"""flag = True                                                       # 是否还需要爬取下一页: 见if interval.days>self.day_before:中的说明div1 = soup.find("div",class_="page")                          # 找到底部页码区域div2 = div1.find_previous_sibling("div")                       # 找到它的哥哥ul = div2.find("ul")                                             # 找到哥哥里的无序列表lis = ul.find_all("li")                                          # 找到无序列表的所有项目news = list()for li in lis:title_label = li.find("span",class_="title")newsURL = "{}/{}".format(self.homeURL,title_label.find("a").attrs["href"].strip("/"))title = self.label_compiler.sub("",str(title_label))        # 用标签正则去除title_label中所有标签, 剩下的即为标题span = li.find("span",class_="lfoote")                      # 找到包含日期与来源的span标签date = str(span.find("p").string)                          # span下面第一个<p>标签包含日期date = self.date_compiler.sub("",date)                         # 使日期变为yyyymmdd格式datetime_date = datetime.strptime(date,"%Y%m%d")       datetime_now = datetime.strptime(self.date,"%Y%m%d")interval = datetime_now - datetime_dateif interval.days>self.day_before:                          # 这里与SEMI中有所区别, 因为SEMI的新闻相对更新较少, 第一页基本上够用了, 但是新材料网的新闻更新很多, 以20191227为例, 新闻大概有3~4页, 因此该函数需要返回一个flag告诉外层调用是否还需要继续下一页flag = Falsebreaksource = str(span.find("p",class_="author").string)           # 第二个<p>标签或者class属性为author的p标签中包含来源news.append({"url": newsURL,"title": title,"date": date,"source": "出自:{}".format(source),})return news,flagdef parse_newscontent(self,session,url):                           # 获取新闻内容: session(新闻)html = session.get(url).textsoup = BeautifulSoup(html,"lxml")div = soup.find("div",class_="newsDetail")"""这边出现一个问题:并不是所有的新闻都是把文字放在<p>标签中的,意外的事情是可能有的新闻是用<br>半标签来分块的, 这就非常讨厌了,"""if div is None:                                                    # 为了防止这个div标签以后消失, 我们以全部的<p>标签作为备用: 这里ps = soup.find_all("p")                                     paragraphs = list()for p in ps:paragraph = self.label_compiler.sub("",str(p))paragraphs.append(paragraph)return paragraphselse:                                                          # 这个名为newsDetail的div若还在, 就直接拿到它的所有信息, 因为文本有的在<p>标签里, 有的在<br>标签中, <br>标签还是个很讨厌的半标签, 不是很好处理, 所以就暴力的把所有标签去除, 剩下的就是新闻内容了string = self.label_compiler.sub("",str(div))return string.split("\n")def main(self,):self.session.get(self.homeURL)                                     # 首先访问主页: page = 0all_news = []while True:page += 1print("正在获取第{}页的新闻...".format(page))news,flag = self.parse_newslist(self.session,self.newestURL.format(page))print("  - 共有{}条新闻".format(len(news)))for i in range(len(news)):print("  - 正在获取第{}条新闻...".format(i+1))url = news[i]["url"]paragraphs = self.parse_newscontent(self.session,url)news[i]["paragraphs"] = paragraphs[:]                     # 注意此处一定要浅复制[:], 否则会导致深复制错误all_news += newsif not flag: breakwith open("{}/{}/{}/XCL_{}.txt".format(self.dirs["新闻"],self.date,self.directorys[1],self.date),"w",encoding="UTF-8") as f:f.write(str(all_news))return all_newsif __name__ == "__main__":xcl = XCL(day_before=1)xcl.main()

Crawl_GGLB.py

# -*- coding: UTF-8 -*-
# Author: 囚生
# 获取高工锂电网(最新资讯板块的内容)from Crawl import Crawlimport re
from requests import Session
from bs4 import BeautifulSoup
from datetime import datetimeclass GGLB(Crawl):def __init__(self,day_before=0,                                                  # 获取几天前的新闻: 默认0即获取今天的数据):  Crawl.__init__(self,)                                            # 继承父类Crawl的成员变量及方法""" 类构造参数 """self.day_before = day_before                                     # 获取几天前的新闻: 默认0为只获取今天的新闻, 一般建议还是设为1, 因为昨天的新闻可能昨天并没有能够全部获取到""" 类常用参数 """self.homeURL = self.urls[2].strip("/")                          # 高工锂电主页URL: 它应该位于websites.csv列表中的第3个self.newestURL = self.homeURL+"/list-0-{}.html"                    # 最新新闻列表URL: "{}"中是空给页码数的self.session = Session()                                        # 类会话对象""" 类初始化 """self.session.headers = {"User-Agent": self.user_agent}          # 为类会话设置用户代理def parse_newslist(self,session,url):                               # 获取新闻列表: session(类会话对象), url(新闻列表的网址URL)html = session.get(url).textsoup = BeautifulSoup(html,"lxml")"""与新材料网一样:通过分析网页发现新闻列表区域在一个<div>标签中,但这个div标签的class属性太长, 如果某天发生改变就会很糟糕,我们同样选择先定位新闻列表底部的1,2,3,4翻页按钮, 因为它的id为pagination是很有特征的, 并且预计不会发生较大改变"""flag = True                                                      # 是否还需要爬取下一页: 见if interval.days>self.day_before:中的说明div1 = soup.find("div",attrs={"id":"pagination"})                # 找到底部页码区域div2 = div1.find_previous_sibling("div")                       # 找到它的哥哥: 即新闻列表区域news_items = div2.find_all("div",class_="news-item")             # 这个新闻列表区域里面会有若干<div class="news-item">, 遍历每个获取需要的信息即可news = list()                                                   # 存放新闻基本信息的列表for news_item in news_items:title_label = news_item.find("div",class_="title")aLabels = title_label.find_all("a")                             # 其实获取第一个就行了, 而且应该只有一个, 为什么我要获取所有的<a>标签呢, 因为担心之后在这里会多出一些<a>标签for a in aLabels:if "art" in a.attrs["href"]:                          # 这表明是我需要的超链接, 新闻内容页面URL结构是: "/art-<编号>"newsURL = "{}/{}".format(self.homeURL,a.attrs["href"])title = self.label_compiler.sub("",str(a))title = title.replace("【","[").replace("】","]")            # 因为我最后发送到微信群里的时候会给标题两端加上"【】", 这个网站大部分新闻前面会有"【原创】", 影响美观, 所以把"【】"替换成"[]"date = str(title_label.find("span",class_="time").string)     # 获取字符串的日期: 注意高工锂电的日期是年月日时分秒, 为了省事我们就获取前8位的年月日了date = self.date_compiler.sub("",date)[:8]                    # 使日期变为yyyymmdd格式datetime_date = datetime.strptime(date,"%Y%m%d")datetime_now = datetime.strptime(self.date,"%Y%m%d")interval = datetime_now - datetime_dateif interval.days>self.day_before:                             # 同新材料网flag = Falsebreaknews.append({"url": newsURL,"title": title,"date": date,})return news,flagdef parse_newscontent(self,session,url):                           # 获取新闻内容: session(新闻)html = session.get(url).textsoup = BeautifulSoup(html,"lxml")spans = soup.find_all("span")source_flag = False                                              # 记录是否在页面上找到了新闻来源: 如果没有则默认来源于高工锂电for span in spans:if str(span.string)[:5]=="文章来源自":source_flag = Truesource = str(span.string)                               # 找到了文章来源, 则使用找到的来源if not source_flag: source = "文章来源自:高工锂电"                  # 找不到则默认div = soup.find("div",class_="content-article")                   # 即便如此, 为了相对精准一些, 我们还是先定位这个class为content-article的<div>标签"""高工锂电的就比较麻烦, 它没有一个<div>标签把所有的新闻文字<p>标签包裹起来, 虽然有一个class为content-article的<div>标签包含了新闻内容, 但它不止是包含新闻文字, 里面还有一些乱七八糟的东西<p>标签极其零散, 但是高工锂电似乎每篇新闻有个简短的摘要, 我觉得意义不大, 而且我不能确定每篇新闻都有这个摘要, """paragraphs = list()                                              # 用于存储段落的列表if div is None: ps = soup.find_all("p")                           # 为了防止这个div标签以后消失, 我们以全部的<p>标签作为备用else: ps = div.find_all("p")                                     # 这个名为content-article的div若还在, 就直接拿到它所有的p标签for p in ps:paragraph = self.label_compiler.sub("",str(p))paragraphs.append(paragraph)return paragraphs,sourcedef main(self,):self.session.get(self.homeURL)                                   # 首先访问主页: 高工锂电page = 0all_news = []while True:page += 1print("正在获取第{}页的新闻...".format(page))news,flag = self.parse_newslist(self.session,self.newestURL.format(page))print("  - 共有{}条新闻".format(len(news)))for i in range(len(news)):print("  - 正在获取第{}条新闻...".format(i+1))url = news[i]["url"]paragraphs,source = self.parse_newscontent(self.session,url)news[i]["paragraphs"] = paragraphs[:]                  # 注意此处一定要浅复制[:], 否则会导致深复制错误news[i]["source"] = sourceall_news += newsif not flag: breakwith open("{}/{}/{}/GGLB_{}.txt".format(self.dirs["新闻"],self.date,self.directorys[2],self.date),"w",encoding="UTF-8") as f:f.write(str(all_news))return all_newsif __name__ == "__main__":gglb = GGLB(day_before=1)gglb.main()

manage.py

# -*- coding: UTF-8 -*-
# Author: 囚生
# 主函数import os
import time
import itchat
from utils import *
from Crawl_SEMI import SEMI
from Crawl_XCL import XCL
from Crawl_GGLB import GGLBGNAME = "Wechat Debug"                                                     # 群名
TIME_INTERVAL = 60                                                      # 间隔多少秒发送一次新闻
DAY_BEFORE = 1                                                          # 获取几天前的数据if __name__ == "__main__":itchat.auto_login(hotReload=True)                                  # 微信登录semi = SEMI(day_before=DAY_BEFORE)xcl = XCL(day_before=DAY_BEFORE)gglb = GGLB(day_before=DAY_BEFORE)# 运行爬虫semi.main()xcl.main()gglb.main()date = semi.date                                                     # 这里用xcl.date, gglb.date也一样directorys = semi.directorys                                        # 这里用xcl.directorys, gglb.directorys也一样    news = []                                                           # 存放所有新闻的列表# 读取爬取到的新闻: 这边写得有点硬with open("news/{}/{}/SEMI_{}.txt".format(date,directorys[0],date),"r",encoding="UTF-8") as f:news += eval(f.read())with open("news/{}/{}/XCL_{}.txt".format(date,directorys[1],date),"r",encoding="UTF-8") as f:news += eval(f.read())    with open("news/{}/{}/GGLB_{}.txt".format(date,directorys[2],date),"r",encoding="UTF-8") as f:news += eval(f.read())# 推送for new in news:                                                    # 遍历每条新闻forward = generate_forward_content(new)                            # 注意该函数还有其他4个默认参数: minlen, maxlen, sent_minlen, keywordsflag = send_group(forward,GNAME)if not flag: raise Exception("没有找到微信群!")time.sleep(TIME_INTERVAL)# 如果需要定时启动: '''while True:if flag and int(time.strftime("%H"))==7:                        # 每天早上七点多开始运行该代码itchat.auto_login(hotReload=True)                          # 微信登录semi = SEMI(day_before=DAY_BEFORE)xcl = XCL(day_before=DAY_BEFORE)gglb = GGLB(day_before=DAY_BEFORE)# 运行爬虫semi.main()xcl.main()gglb.main()date = semi.date                                             # 这里用xcl.date, gglb.date也一样directorys = semi.directorys                                # 这里用xcl.directorys, gglb.directorys也一样        news = []                                                   # 存放所有新闻的列表# 读取爬取到的新闻: 这边写得有点硬with open("news/{}/{}/SEMI_{}.txt".format(date,directorys[0],date),"r",encoding="UTF-8") as f:news += eval(f.read())with open("news/{}/{}/XCL_{}.txt".format(date,directorys[1],date),"r",encoding="UTF-8") as f:news += eval(f.read())    with open("news/{}/{}/GGLB_{}.txt".format(date,directorys[2],date),"r",encoding="UTF-8") as f:news += eval(f.read())# 推送for new in news:                                            # 遍历每条新闻forward = generate_forward_content(new)                    # 注意该函数还有其他4个默认参数: minlen, maxlen, sent_minlen, keywordsflag = send_group(forward,GNAME)if not flag: raise Exception("没有找到微信群!")time.sleep(TIME_INTERVAL)time.sleep(50000)                                            # 睡50000秒(这样就不会说7:00运行代码, 7:10发现int(time.strftime("%H"))还是7, 又运行一次)else:time.sleep(600)                                               # 50000秒后: 十分钟检查一次是否到过7点了'''    pass

utils.py

# -*- coding: UTF-8 -*-
# Author: 囚生
# 一些常用的工具函数import re
import time
import itchat
#from jieba import analyseCONTENT_REGULAR = re.compile(r'[ |\r|\n|\t|\xa0]')def sent_tokenize(x):                                                     # 中文分句sents_temp = re.split('(。|!|\!|?|\?)',x)sents = []for i in range(len(sents_temp)//2):sent = sents_temp[2*i] + sents_temp[2*i+1]sents.append(sent)return sentsdef generate_forward_content(news_info,                                     # news_info为一个字典, 里面有五个键值对: url, title, date, paragraphs, sourcesent_minlen=20,                                                        # 每句话的最少字符数(用于过滤废话和无关信息)minlen=300,                                                            # 推送文字的最小长度maxlen=500,                                                             # 推送文字的最大长度keywords=None,
):                                                                       # 根据新闻信息字典生成推送内容: 若不传入keywords列表则默认不进行关键词匹配url = news_info["url"]title = news_info["title"]date = news_info["date"]paragraphs = news_info["paragraphs"]source = news_info["source"]source = CONTENT_REGULAR.sub("",source)content = str()for paragraph in paragraphs:# 这边是可以利用jieba进行关键词提取, 但是我测试结果发现基本上都跟需要的关键词对不上# a = analyse.extract_tags(paragraph,topK=20,withWeight=True,allowPOS=('n',"nr","ns"))# for i in a: print(i)string = CONTENT_REGULAR.sub("",paragraph)string = string.strip()sents = (sent_tokenize(string))sents = list(filter(lambda x: len(x)>=sent_minlen, sents))for sent in sents:temp = content+sentif len(temp)>maxlen:now = time.strftime("%H:%M")if len(content)>minlen: return "{}\n【{}】\n发布时间: {}\n{}\n({})\n{}".format(now,title,date,content,source,url)else:content = tempreturn "{}\n【{}】\n发布时间: {}\n{}\n({})\n{}".format(now,title,date,content,source,url)content = tempnow = time.strftime("%H:%M")return "{}\n【{}】\n发布时间: {}\n{}\n({})\n{}".format(now,title,date,content,source,url)def send_group(message,gname):                                             # 发送message到群名为gname的群rooms = itchat.get_chatrooms(update=True)                           # 更新微信群: 好像没什么用, 还得是手动发一下消息把微信群调出来flag = Falsefor room in rooms:if room["NickName"]==gname:flag = Trueroom_id = room["UserName"]itchat.send(message,room_id)return flagreturn flagif __name__ == "__main__":pass

(完结)

【项目完结】笑靥如春三冬暖,嫣语似晴沉霾散。西子湖畔梦犹然,情起缘尽余心安。相关推荐

  1. 愿你三冬暖,愿你春不寒

    愿你一生有山可靠,有树可栖, 与心爱之人,春赏花, 夏纳凉,秋登山,冬扫雪. 愿你在海边踏沙,有良人为伴. 愿你在山巅眺望云雾,有痴傻的他紧拉. 愿你如愿遇到命中的缘分, 不早也不晚,不急也不缓. 愿 ...

  2. 【第3天】良言一句三冬暖,恶语伤人六月寒

    正文 良言一句三冬暖,恶语伤人六月寒 语言不仅可以传递信息,更是一种情感的交流.热情.友善的态度可以让交谈更加顺畅:冷淡.无礼的话语会像利刃一样刺伤对方.不同的说话态度,会造成不同的交谈效果.一句宽慰 ...

  3. 愿你三冬暖愿你春不寒--喜欢的诗

    单纯的因喜欢,而收藏 原文吧 愿你三冬暖,愿你春不寒;愿你天黑有灯,下雨有伞.愿你路上有良人相伴,愿你所有快乐 无需假装,愿你此生尽兴.赤诚善良.愿时光能缓,愿故人不散;愿有人陪你颠沛流离,愿你惦念的 ...

  4. vue项目刷新当前页面的三种方法

    本文介绍了vue项目刷新当前页面的三种方法,本文图文并茂给大家介绍的非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下. 想必大家在刨坑vue的时候也遇到过下面情形:比如在删除或者增加一条记录的时 ...

  5. Android基础——项目的文件结构(三)

    Android基础--项目的文件结构(三) 代码源文件夹与资源文件夹 [注]此项目文件结构仅限于Android Studio下的Android项目!!! 在一个Android项目中,代码源文件夹有4个 ...

  6. Laravel大型项目系列教程(三)之发表文章

    Laravel大型项目系列教程(三)之发表文章 一.前言 上一节教程中完成了用户管理,这节教程将大概完成发表Markdown格式文章并展示的功能. 二.Let's go 1.数据库迁移 文章模块中我们 ...

  7. a标签跳转后关闭当前页面_微信小程序2020-day-2 导航项目(跳转三种形态)

    day-2 导航项目(跳转三种形态) 转发跳转:保留当前页面,跳转新页面,可返回 重定向跳转:关闭当前页面,跳转,不可返回,不能指定标签导航页面 跳转标签导航:跳转到标签导航页面,标签导航跟随选中 a ...

  8. 工作人员做好项目协调服务器,项目团队协作做好三件事

    原标题: 项目团队协作做好三件事 大家也许在小时候就听过三个和尚的故事:当庙里有一个和尚时,他一切自己做主,做得很自在;当庙里有两个和尚时,他们通过协商可以自觉地进行分工合作,同样做的不错;可当庙里来 ...

  9. CV项目肢体动作识别(三)内附完整代码和详细讲解

    CV项目肢体动作识别(三)内附完整代码和详细讲解 首先我还是给出完整的代码,然后再进行详细的讲解.这一次我们用模块化的思想,把一个功能模块化(moudle),这种思想在工程中非常常见,在分工中你需要做 ...

最新文章

  1. shell脚本_查找无效网址
  2. php为什么学的人越来越少,为什么PHP这么受欢迎?
  3. toad dba suite for oracle 12.1,Toad for Oracle 12.1下载地址
  4. iOS/OS X内存管理(一):基本概念与原理
  5. 服务器集群技术(备份服务器方案和均摊工作方案)(用来解决服务器挂掉问题)...
  6. BC:带你温习并解读《中国区块链技术和应用发展白皮书》—概述
  7. Nginx编译安装和平滑升级
  8. 吴恩达机器学习视频学习笔记(4)
  9. 熊出没之奇幻空间里面的机器人图片_《熊出没之奇幻空间》里面令人触动的两个角色...
  10. 数据分析报告怎么写(四)
  11. 2021-07-26记录字节“懂车帝”重庆岗一面(绝对凉)
  12. ESP8266连接阿里云控制LED灯
  13. 电脑中显示dns服务器可能不可用,Win7网络诊断“DNS服务器可能不可用”怎么解决?-电脑自学网...
  14. MyEclipse2017使用maven搭建SSM项目
  15. 【yolov5检测代码简化】Yolov5 detect.py推理代码简化,输入图片,输出图片和结果
  16. 计算机局域网的组建与应用论文,校园局域网的组建(毕业论文)
  17. 认识CoreData-基础使用
  18. 十个健脑绝招 锻炼大脑 提高记忆(转)
  19. 怎么样才能做好非标自动化设备的设计?|| 技巧总结
  20. 统计学必知!「标准差方差」之间不得不说的关系

热门文章

  1. 家居用品适合B2C直销模式吗?
  2. JAVA微信小程序图书馆座位预约小程序系统毕业设计 开题报告
  3. 亚马逊站内deal申请条件你都知道?
  4. 【目标管理】企业目标如何落地?
  5. python六角形的绘制 编程_Python绘制六角形
  6. 智能辅助办案,给现代包公减负!
  7. 如何关闭ThinkPad电脑的触摸板
  8. ps4将图片弄成透明图片
  9. 智能指针相关问题解答
  10. linux查看kafka队列消息,Kafka消息队列-从开始到上线