前言
最近在追小说,没事的时候会时不时的去看看有没有更新,于是乎写个脚本帮我看看有没有更新不就行了。
正文
实现思路
首先找到我们平时看小说的网站,爬取该小说的目录页面,检查章节是否更新,更新则发送邮件通知自己,否则继续爬取检查。
具体实现
我是用的网站是这个,(盗版小说网站,有能力请支持正版)。
1.首先在搜索栏搜索你的小说,查看是否有你在追的小说
2.仔细分析url你会发现每个小说都有一个或多个id,格式如下。15_15698、12_12366等。
3.提取出最新章节数,对比你看到的章节数,大于则认为是更新。
4.提取内容发送邮件(至于怎么发送百度有很多教程)
如果你也用这个网站看小说可以直接稍微修改一下我的源码即可使用,代码附在文章最后
代码运行
尽然是脚本帮你查看小说有没有更新,那么你肯定得让脚本能够一直运行,所以你有个云服务器肯定是最好的,毕竟自己的电脑总要关机的。我是把自己的代码挂在自己的服务器上。linux运行命令示例如下nohup python3 -u listen.py -a 12_12585 -n 1000 --email=111111@qq.com > novel.log &
命令解释
nohup是在你退出远程连接终端时程序不会被kill(ps -aux可以看到正在运行的程序,自己可以试试)
> novel.log &是挂在后台执行并将输出重定向到novel.log文件里
-a -n --email 这些是输入的参数
python3 -u -listen.py 用python3环境运行listen.py脚本,-u代表实时输出到文件,不然查看novel.log时可能是空文件
(本来是想着用django写个网站让部分没有服务器的读者也能使用小说通知这个脚本的,但是开发到一半就一直没时间,所以没服务器的挂在本机或者另想办法吧)
总结
之前用的一直是beautifulsoup库解析获取数据,但是找工作时发现挺多会问懂不懂xpath的,所以这次正好用用xpath。
然后就是学会用命令行输入参数了以及nohup和ps这些命令,虽然我的博客都挺小儿科的(绝不抄袭,坚决原创),但是在娱乐之余还能学点东西何乐而不为呢。
import smtplib
from email.mime.text import MIMEText
# email 用于构建邮件内容
from email.header import Header
# 用于构建邮件头
from lxml import etree
import requests
import re
import time
import getopt
import sys
def send_mail(content):
# 发信方的信息:发信邮箱,QQ 邮箱授权码
from_addr = 'XXXXXX'
password = 'XXXXXXX'
# 发信服务器
smtp_server = 'smtp.qq.com'
# 邮箱正文内容,第一个参数为内容,第二个参数为格式(plain 为纯文本),第三个参数为编码
msg = MIMEText(content,'plain','utf-8')
# 邮件头信息
msg['From'] = Header(from_addr)
msg['To'] = Header(to_addr)
msg['Subject'] = Header('更新了')
# 开启发信服务,这里使用的是加密传输
server = smtplib.SMTP_SSL(host=smtp_server)
server.connect(smtp_server,465)
# 登录发信邮箱
server.login(from_addr, password)
# 发送邮件
server.sendmail(from_addr, to_addr, msg.as_string())
# 关闭服务器
server.quit()
#爬取文章内容
def get_content(urls):
global new_art
new_art = new_art + int(len(urls))
all_content = ''
#因为是从最新的章节获取,所以倒序迭代
for detial in reversed(urls) :
#网络波动有时请求会失败,暂停一会在请求
while True:
url = host + detial
try:
res = requests.get(url=url,headers=headers)
except:
time.sleep(60)
else:
break
res.encoding = "utf-8"
html = etree.HTML(res.text)
title = html.xpath('//div[@class="bookname"]/h1/text()')
all_content = all_content + title[0] +'\n' + '网页链接:' + url + '\n'
content = html.xpath('//div[@id="content"]/text()')
#删除多余的无用字符
content = re.sub(r'[(\\u3000)\']','',str(content),count=0,flags=re.M|re.I)
all_content = all_content + content + '\n'
return all_content
#通过标题获取章节数
def get_new_art_num(text):
art_num = re.search('\d+',text[0])
return int(art_num.group(0))
#获取该网页最新的五章,检查是否更新
def get_url(a_id):
url = host + a_id + '/'
re = requests.get(url=url, headers=headers)
re.encoding = "utf-8"
tree = etree.HTML(re.text)
new_art_url = []
#获取最新的五章
#正常小说一天也不会更新超过五章,如果有自己修改
for i in range(1,6):
i = str(i)
art_title = tree.xpath('//div[@id="list"]//dd[' + i + ']/a/text()')
art_url = tree.xpath('//div[@id="list"]//dd[' + i + ']/a/@href')
if get_new_art_num(art_title) <= new_art :
if len(new_art_url) == 0 :
return 0
else:
return new_art_url
else :
new_art_url.append(art_url[0])
def useage():
print(
'''
Usage:
-a 小说的id(必填)
-n 你看到的最新章节(必填)
-b 程序每天开始运行时间
-e 结束时间
--email 接收通知邮箱(必填)
''')
if __name__ == '__main__':
if len(sys.argv) < 2:
useage()
sys.exit(3)
host = 'https://www.78zw.com/'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0',
'host': 'www.78zw.com',
}
#初始化,无输入运行默认值
#我的小说一般在下午一点到七点更新,过了七点还没更新认为他今天不更新
begin_time = 13
end_time = 19
# 收信方邮箱等必填信息
a_id = new_art = to_addr = None
try:
opts, args = getopt.getopt(sys.argv[1:], 'a:n:b:e:', ['email='])
for name,value in opts:
if name == '-a':
a_id = value
if name == '-n':
new_art = int(value)
if name == '-b':
begin_time = int(value)
if name == '-e':
begin_time = int(value)
if name == '--email':
if re.match(r'[\d\w_-]+@[\d\w_-]+(\.[\d\w_-]+)+',value) == None:
print('邮箱格式错误')
sys.exit(3)
to_addr = value
except getopt.GetoptError:
useage()
if not (a_id and new_art and to_addr):
print('信息输入不完整')
useage()
sys.exit(3)
while True:
if begin_time <= int(time.strftime('%H',time.localtime(time.time()))) < end_time :
urls = get_url(a_id)
if urls == 0 :
print(time.strftime('%m{m}%d{d} %H:%M', time.localtime(time.time())).format(m='月',d='日'),':无更新')
#每十分钟爬取一次
#可以修改爬取频率,不能间隔太短,一方面是给服务器造成压力,一方面是很可能触发反爬等机制
time.sleep(60*10)
continue
# 如果更新,发送到邮箱,更新所看的最新章节,并且今天不在爬取
send_mail(get_content(urls))
sleep_time = begin_time + 24 - int(time.strftime('%H',time.localtime(time.time())))
print(time.strftime('%m{m}%d{d} %H:%M', time.localtime(time.time())).format(m='月',d='日'), "今日已更新,程序休眠中...")
time.sleep(sleep_time*60*60)
else :
print(time.strftime('%m{m}%d{d} %H:%M', time.localtime(time.time())).format(m='月',d='日'),"还没到点,再睡会...")
time.sleep(60*60)