分类 Python 下的文章

前言

树莓派相信熟悉小型Linux开发板的朋友都不陌生,作为一款优秀的卡片型小电脑,麻雀虽小五脏俱全,普通电脑能做的事情,它全都能做,最重要的一个优点,它作为一台24小时不停服的服务器来讲,低功耗,高收益,所以民间高手用树莓派来diy各种千奇百怪好玩好用的东西,例如用树莓派做家庭nas存放自己的小姐姐,用树莓派做为软路由管理家庭的网络,用树莓派做自己的贴心语音小助手来管理家庭智能家电等等一系列的应用场景,可玩性很高,作为一个很爱折腾的我也是对它爱不释手,很多人买来树莓派就是放在角落吃灰,但我的树莓派至今已经平稳运行了2年之久,它承担了我的毕业设计---智能语音机器人,承担了家中的nas共享,承担了平时在家的jupyter开发环境,在今天,我将赋予它新的使命---扶墙(FQ)好帮手,让我们家庭网络的终端中,无需连接威批恩便可以顺畅睁眼看世界!接下来请跟随文章的脚步,一起走进奇妙的折腾之旅~~~

本篇教程会占用你大概20分钟左右,过程较为复杂,但只要坚持做完,你会发现自己的扶墙是多么的方便!

准备

  • 一台刷好系统可以正常工作的树莓派
  • shadowsocksR订阅链接(一般从机场主处购买获得)
  • 树莓派安装Python环境

开始

  1. 使用ssh工具链接进入到树莓派

  1. 安装必备依赖
sudo apt install python3-pip
sudo apt install net-tools
  1. 从github下载ssr-command-client源码,关于ssr-command-client的信息,详情请见https://github.com/TyrantLucifer/ssr-command-client
git clone https://github.com/TyrantLucifer/ssr-command-client.git
  1. 进入到项目文件夹中,安装ssr-command-client相关依赖
sudo pip3 install -r requirement.txt
  1. 设置ssr-command-client订阅链接
python3 main.py --setting-url 你的订阅链接地址
  1. 更新订阅列表
python3 main.py -u

  1. 查看节点列表是否正常
python3 main.py -l

  1. 设置代理IP地址
  • 查看本机ip
sudo ifconfig

记住这个IP地址

  • 修改配置项
python3 main.py --setting-address 上面的ip地址
  1. 使用第七步显示的节点id,选择一个id,生成配置json文件
python3 main.py -c 1

  1. 启动代理
python3 main.py -s

享用

按照图中指示设置内容,点击左下角应用选项,即可设置完毕

  • 设置代理

选择我们刚才设置好的代理方式

打开谷歌,愉快的网上冲浪

  • QQ浏览器,与Google浏览器设置方法一致

Tips

  • 我没有订阅链接,但我还是想体验扶墙快感怎么办?ssr-command-client项目自带github免费订阅链接,下载项目到本地后直接执行python3 main.py -l即可
# github共享ssr订阅链接
https://raw.githubusercontent.com/satrom/V2SSR/master/SSR/Day.txt
  • 我还想手机也能实现跟电脑一样的功能,别着急,下期更新~~~
  • 我没有树莓派怎么办,云服务器也可以实现这个功能,把ip换成公网ip即可,也是一样的步骤去进行设置
  • 我没看懂怎么办,如果有需要的话我可以出一期教学视频给大家演示一遍,这看后台读者的需
  • 我想停止代理,更换节点怎么办?使用python3 main.py -S停止代理,使用python3 main.py -c 新的节点id重新生成节点配置文件,使用python3 main.py -s启动代理
  • 我想实现网站自动分流,墙外网站走代理,墙内网站走直连,别着急,下期更新~~~
  • 更多好玩的教程,好玩的技术博客,请持续关注下方我的微信公众号,如果有好的建议和好的想法欢迎公众号后台留言或者发送邮件至TyrantLucifer@linuxstudy.cn,我们下期再见!

我的微信公众号

前言

ShadowsocksR,小名酸酸乳,在扶墙届具有着不小的名声,如今机场的大半江山都被这款知名代理工具所占据,我平时的工作学习环境是个人笔记的Linux系统(Ubuntu 18.04),每日都有着上Google的需求,所以扶墙出去也是每日的必需操作,然而这款代理工具发展到至今,一键配置的脚本有很多,Windows下客户端有很多,唯独就是没有Linux环境下的客户端,之前倒是是有一款(electron-ssr),但不久前作者彻底删除了项目,估摸着是被叫去喝茶了,只剩下v0.2.6版本孤零零的存活于世,我曾经尝试过这款软件,使用体验还不错,界面化操作,适用于小白玩家,界面点点点,但作为一名硬核的Linux玩家,命令能实现的骚操作为什么不能用命令行实现呢?GUI在Linux下简直是浪费,为了满足我平时的需求,能够在命令行下直接一键开启代理,更换节点,ssr-command-client应运而生。

介绍

ssr-command-client是我开发的一款基于Python3的酸酸乳命令行客户端,功能简洁,主要是针对于Linux用户在命令行下能够愉快的使用代理上网冲浪,定位清晰,不臃肿,轻量级,没有GUI的限制,可以使用在国内的云服务器(阿里云、腾讯云)中,国内云服务器普遍存在一个问题,git速度很慢很慢,以至于我们在安装一些开源组件从github上拉取源码的过程变得十分缓慢,通常以10KB为秒的计数单位,而ssr-command-client完美解决了这个问题,世界可达,项目地址:https://github.com/TyrantLucifer/ssr-command-client(跪求star)

特性

ssr-command-client主要有以下功能:

  • 支持订阅链接解析
  • 支持指定本地socks5代理端口
  • 支持测试节点ping值

安装方式

git clone https://github.com/TyrantLucifer/ssr-commend-client.git
cd ssr-commend-client
pip3 install -r requirement.txt

使用方法

python3 main.py [OPTIONS]

OPTIONS

-l --list "show ssr list" 展示ssr节点列表
-s --start "start ssr proxy" 启动ssr代理服务
-S --stop "stop ssr proxy" 停止ssr代理服务
-p --port port "assign local port" 指定本地代理端口
-c --config ssr_node_id "generate config json file" 生成指定节点json文件
-u --update "update ssr list" 更新ssr节点列表

效果展示

  • 输出ssr链接节点列表 python3 main.py -l

  • 更新ssr订阅链接 python3 main.py -u

  • 生成ssr节点配置文件 python3 main.py -c ssr_node_id

  • 指定本地代理端口并生成配置文件 python3 main.py -c ssr_node_id -p local_port

  • 启动ssr代理 python3 main.py -s

  • 停止ssr代理 python3 main.py -S

未来计划

  • [ ] 支持多订阅链接解析
  • [ ] 支持自动选择速度最优节点
  • [ ] 支持自动生成PAC代理文件
  • [ ] 支持一键关闭、开启系统PAC网络代理(针对于Ubuntu 18.04)
  • [ ] 支持指定ssr链接启动服务
  • [ ] 支持命令行解析ssr链接信息

总结

没有轮子造轮子,这是我一概的作风,也是我学习编程的主要驱动力,兴趣占据了大半,我想做出一些让大多数人都觉得好用的东西,方便自我也方便他人,到今天为止这是我写的第44篇博客,但是发在公众号的只有几篇,未来我会坚持更新,记录自己的代码人生,code is my life!

Tips

如果有好的建议,欢迎发邮件给我,关注下方我的个人微信公众号在后台留言,或者在项目主页提交issue,如果觉得好用的话,各位读者姥爷在github项目首页帮我点个star,右下角帮我点击在看,感激不尽!

我的微信公众号

前言

2020是一个不太平的年头,篮球标杆的意外去世,新型冠状病毒的肆虐横行,给本来喜庆的新年蒙上了厚厚的阴霾,为此,国家呼吁减少出行,安心呆在家里为国家做贡献,之前想象中的躺在床上有吃的有WiFi的生活彻底实现了,但是躺的时间太久了,难免有些厌倦,朋友圈里的有些朋友丧心病狂的去晒自己吃小橘子的步骤,简直惨不忍睹,为了打发时间,想了想要不爬一下最近全国肺炎感染信息,小小分析一下,画几个图出来看看感染情况究竟如何。

采集步骤

圈里的朋友最近都会分享丁香医生的接口https://3g.dxy.cn/newh5/view/pneumonia,就从这个网址入手吧

首先浏览器打开网址,熟练的操作F12,看到以下些个请求:

请求列表

有没有异步ajax请求呢?发现没有,看来页面开发的哥们没有直接开发接口,继续看页面数据怎么渲染上去的,点开主要的页面请求,看一看response,发现以下内容:

返回页面信息

原来开发页面哥们直接用js渲染json到页面上了,那就更简单了,直接get请求发送,解析标签获取内容即可,获取json直接保存到数据库,思路有了,接下来设置一下表结构吧:

create table sars_real_num(
    parent_id varchar(20), --父级地区
    name varchar(20), --省市名字
    confirmedCount integer, --确诊人数
    deadCount integer, --死亡人数
    suspectedCount integer, --疑似人数
    curedCount integer, --治愈人数
    update_time datetime --更新时间
);

之后直接上代码:

run.py

# 导入相关包
import requests
import json
import re
import datetime
from pymysql import *
from bs4 import BeautifulSoup

# 设置url
url = 'https://3g.dxy.cn/newh5/view/pneumonia'

# 发送http请求,获取结果
result = requests.get(url, headers=headers)

# 获取更新时间
update_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')

# 解析内容
result.encoding='utf-8'
content = result.text
soup = BeautifulSoup(content, 'lxml')
script_tags = soup.find_all(id='getAreaStat')
r = script_tags[0].text[27:-11]
province_list = json.loads(r)

# 设置数据库连接信息
host = '*.*.*.*'
port = 3306
database = 'sars'
user = 'sars'
password = 'sars'
charset='utf8'
connection = connect(host=host, port=port, database=database, 
               user=user, password=password, charset=charset)
cursor = connection.cursor()
insert_sql = 'insert into sars_real_num(name, parent_id, confirmedCount, deadCount, suspectedCount, curedCount, update_time) values(%s, %s, %s, %s, %s, %s, %s)'

# 解析json,保存到数据库
for province in province_list:
    params = list()
    params.append(province['provinceName'])
    params.append('NULL')
    params.append(province['confirmedCount'])
    params.append(province['deadCount'])
    params.append(province['suspectedCount'])
    params.append(province['curedCount'])
    params.append(update_time)
    cursor.execute(insert_sql, params)
    for city in province['cities']:
        params = list()
        params.append(city['cityName'])
        params.append(province['provinceName'])
        params.append(city['confirmedCount'])
        params.append(city['deadCount'])
        params.append(city['suspectedCount'])
        params.append(city['curedCount'])
        params.append(update_time)
        cursor.execute(insert_sql, params)
connection.commit()

# 关闭数据库连接
cursor.close()
connection.close()

为了使数据具有时效性,我们可以使用schedule这个模块,设置定时任务,每小时收集一次:

get_sars_real_num.py

import requests
import json
import re
import datetime
import schedule
import time
from pymysql import *
from bs4 import BeautifulSoup

def run():

    url = 'https://3g.dxy.cn/newh5/view/pneumonia'

    headers = {
        'User-Agent':'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36',
        'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
        'Accept-Encoding':'gzip, deflate, br',
    }

    result = requests.get(url, headers=headers)

    update_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')

    result.encoding='utf-8'

    content = result.text

    soup = BeautifulSoup(content, 'lxml')

    script_tags = soup.find_all(id='getAreaStat')

    r = script_tags[0].text[27:-11]

    province_list = json.loads(r)

    host = '*.*.*.*'
    port = 3306
    database = 'sars'
    user = 'sars'
    password = 'sars'
    charset='utf8'

    connection = connect(host=host, port=port, database=database, 
                   user=user, password=password, charset=charset)
    cursor = connection.cursor()

    insert_sql = 'insert into sars_real_num(name, parent_id, confirmedCount, deadCount, suspectedCount, curedCount, update_time) values(%s, %s, %s, %s, %s, %s, %s)'

    for province in province_list:
        params = list()
        params.append(province['provinceName'])
        params.append('NULL')
        params.append(province['confirmedCount'])
        params.append(province['deadCount'])
        params.append(province['suspectedCount'])
        params.append(province['curedCount'])
        params.append(update_time)
        cursor.execute(insert_sql, params)
        for city in province['cities']:
            params = list()
            params.append(city['cityName'])
            params.append(province['provinceName'])
            params.append(city['confirmedCount'])
            params.append(city['deadCount'])
            params.append(city['suspectedCount'])
            params.append(city['curedCount'])
            params.append(update_time)
            cursor.execute(insert_sql, params)
    connection.commit()
    cursor.close()
    connection.close()


if __name__ == "__main__":
    schedule.every(1).hours.do(run)
    while True:
        schedule.run_pending()
        time.sleep(1)

在Linux服务器后台设置后台任务,让代码自己跑起来:

nohup python3 get_sars_real_num.py >> get_sars_real_num.log 2>&1 &

在后台数据库我们可以看见数据一点一点的保存了进来:

数据库信息

数据简单分析

作为典型,我们拿出湖北省的数据来进行画图分析,在这里我们用到了Python比较经典的画图库matplotlib,有兴趣的小伙伴可以去官网学习库的用法,在这里不多过赘述。

大体流程:查询数据->画图

analysis.py

import matplotlib.pyplot as plt
from matplotlib.pyplot import MultipleLocator
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['font.serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
plt.rcParams['figure.figsize'] = (14.0, 10.0)
from pymysql import *
host = '60.205.181.33'
port = 3306
database = 'sars'
user = 'sars'
password = 'sars'
charset='utf8'
connection = connect(host=host, port=port, database=database, 
               user=user, password=password, charset=charset)
cursor = connection.cursor()
select_sql = 'select * from sars_real_num where name = %s'
params = list()
params.append('湖北省')
count = cursor.execute(select_sql, params)
results = cursor.fetchall()
confirmedCount = list()
deadCount = list()
suspectedCount = list()
curedCount = list()
update_time = list()
for result in results:
    update_time.append(result[-1].strftime('%Y-%m-%d %H:%M:%S'))
    confirmedCount.append(result[2])
    deadCount.append(result[3])
    suspectedCount.append(result[4])
    curedCount.append(result[5])
plt.plot(update_time,confirmedCount,linewidth=3, linestyle='-.', label='确诊人数')
plt.title("湖北省肺炎确诊情况统计图",fontsize=20)
plt.tick_params(axis='both',labelsize=15)
plt.xticks(update_time, rotation=90)
y_major_locator=MultipleLocator(200)
ax=plt.gca()
ax.yaxis.set_major_locator(y_major_locator)
plt.ylim(0,5000)
plt.legend()
plt.show() #显示折线图

肺炎感染人数统计图

plt.plot(update_time,deadCount,linewidth=3, linestyle='-.', label='死亡人数')
plt.plot(update_time,suspectedCount,linewidth=3, linestyle='-.', label='疑似人数')
plt.plot(update_time,curedCount,linewidth=3, linestyle='-.', label='治愈人数')
plt.title("湖北省肺炎死亡治愈情况统计图",fontsize=20)
plt.tick_params(axis='both',labelsize=15)
plt.xticks(update_time, rotation=90)
y_major_locator=MultipleLocator(10)
ax=plt.gca()
ax.yaxis.set_major_locator(y_major_locator)
plt.ylim(0,200)
plt.legend()
plt.show()

肺炎死亡人数统计图

结论

丁香医生的接口大概在早上7点左右更新数据,且湖北省的整体感染人数、死亡人数都在不断的增加,即将要达到一个爆发期,特殊时期,谨慎出行,出门戴口罩,减少疾病的传播,保持一个健康的身体才是最重要的,祝愿祖国能早日度过这个难关,闲来无事,水文一篇,求各位大佬轻喷,溜了溜了,继续躺着去了~~~

老司机教你用树莓派搭建Jupyter Web开发环境

前言

近期,从箱子里找出了毕业设计用的开发板神器---树莓派(Raspberry Pi),用它做的语音机器人还拿到了学校为数不多的优秀毕业设计,现在回想起来还是很激动,前几篇文章介绍过用树莓派搭建共享存储smb服务、实现内网穿透,今天将介绍用树莓派搭建好用的Python开发环境---Jupyter Notebook,废话不多说,让我们开始吧!

Jupyter Notebook简介

Jupyter Notebook(此前被称为 IPython notebook)是一个交互式笔记本,支持运行 40 多种编程语言。

Jupyter Notebook 的本质是一个 Web 应用程序,便于创建和共享文学化程序文档,支持实时代码,数学方程,可视化和 markdown。 用途包括:数据清理和转换,数值模拟,统计建模,机器学习等等

安装配置

  • ssh登录到树莓派,并切换到root用户,安装Jupyter

这里说一下为什么用root,博主亲自实验过,如果用pi用户的话,Jupyter是写不到环境变量中的,可执行程序无法找到,也就启动不了Jupyter

sudo su
pip3 install jupyter
pip3 install jupyter_contrib_nbextensions
pip3 install six=1.11.0
jupyter contrib nbextension install

执行完上面的命令之后,命令行一直在输出,等到结束之后,就代表安装完毕,然后切换回pi用户,用普通用户执行下面的步骤

  • 生成配置文件

    jupyter notebook --generate-config
  • 生成密码

    jupyter notebook password

执行这条命令之后,命令行会让你输入密码,自行设置这个密码,并记住这个密码,一会Web登录要用到

  • 编辑配置文件

    1. ~/.jupyter/jupyter_notebook_config.json文件中复制出password后面的字符串

    1569597732956

    1. ~/.jupyter/jupyter_notebook_config.py文件中加入以下内容

      c.NotebookApp.ip = '*'
      c.NotebookApp.allow_remote_access = True
      c.NotebookApp.password = u'填上面复制出来的字符串'
      c.NotebookApp.port = 8888
      c.NotebookApp.open_browser = False
      c.NotebookApp.notebook_dir = '/home/pi/jupyter_notebook'

创建可持续化服务文件

切换到root用户,执行

vim /lib/systemd/system/jupyter.service

在文件中添加以下内容:
[Unit]
Description=jupyter_notebook service
After=network.target syslog.target
Wants=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/jupyter notebook --allow-root

[Install]
WantedBy=multi-user.target

启动服务

systemctl start jupyter

systemctl enable jupyter

享用

在浏览器中输入树莓派ip:8888,输入密码,就可以享用你的Jupyter了,开始愉快的编程吧~~~

1569598294810

前言

近期,从Boss那里领取了一份爬数据的任务,刚开始接到任务的时候,感觉应该很简单,页面是静态页面,不用js渲染,也不用自己构造请求参数,就是个从网页文本中用标签选择器咔咔一顿乱选,最后整理存入mongoDB即可,导出csv,完成任务。这本来是我心目中完美的一套流程,但是,随着更加深入的进入到工作节奏中之后,我才发现事情并没有那么简单~~~(大众点评的前端,你真可爱)

前期准备

古人云:工欲善其事,必先利其器。一般网络爬虫,比较经典的几个库是必须的,例如:lxmlrequestsBeautifulSoup,先甭说其他的,先装吧

pip3 install lxml
pip3 install requests
pip3 install beautifulsoup4

在准备好我们的利器之后,接下来肯定就是开始搞事情~~~

开搞第一波

熟练的在浏览器中输入http://www.dianping.com,然后随便打开一个店,比如这个

1565527644309

熟练的点击登录,拿手机扫码登录,出现上图~~~

随着下拉页面,我们终于看到了我们所需要的评论数据,比如这样:

1565527767039

来,摁一下F12,右击检查来瞧瞧这些可爱的小宝贝,到底在html里面是怎么存在的,此时发出了志在必得的嘲笑声~~~哈哈哈哈哈

然后~~~

1565527909029

这是什么??此刻黑人问号脸??在页面上不是TMD显示是字吗?这个svgmtsi是什么标签,我的网页设计白学了?我记着我当时学习挺认真的啊?大兵老师教的挺好呀????????????

img

第一次尝试,失败!Game Over!

第二次尝试

在进行第一次尝试之后,发现大众点评这个网站并不是用普通方式渲染的,是用特定的标签进行渲染上去的,这个标签到底是什么呢?让我很是费解,从来没见过,我该如何下手?既然是不认识的东西,那么看看它的属性?在CSS是怎样表示的。

1565528442040

看了一下这个标签,在CSS中只有一个background属性,给了两个坐标,难道字是用图片拼的?带着怀疑的心情,看一看这个background究竟是何方神圣,打开那个url一看,是这样的情况:

1565529438230

看到了这一个个熟悉的字,果然是通过图片渲染到页面上的,但是,另外一个难题出现了,这个字究竟是怎么对应上去的,此时还有一个信息没有用,那就是上面CSS发现的坐标值,看一看这个svg的源码吧,看看有没有什么发现,打开源码,是这样的~~~

1565529694884

看到了,x和y的值,是不是跟上面的background有一定的关系,此时发挥出小时候做找规律数学题的技能,找一找规律~~~

1565532374180

我们来看一下这个字的坐标:-434-1512,在svg源码中搜索这个字

1565533409422

字在这一行中是第32个字,同时这一样的y坐标是1535,在CSS中它的属性y坐标是-1512,如果将坐标取正值,正式坐标差23,推测一下,y坐标计算是通过background属性y取正加23得到,我们验证一下其他字,发现也是这个规律,至此,我们推测出,y坐标的对应关系;现在还剩下最后一个难题,x的坐标该怎么对应?此时发现svg上面有这么一个属性:

1565533863617

字号是14px,是不是字数*14就等于x坐标了呢?求证一下,年是第32个字,但是31 * 14 = 434,所以大胆猜测一下,x坐标是这个字在这一行的(第几位-1)* 14所得,后来发现其他字也是这样,推测正确。

第二次尝试~~~成功!

获得结论

每个标签的background属性对应着svg中的位置,首先计算过程是将x、y取正,用x / 14 所得的值+1,就是这个标签所代表字在这一行的第几位,用 y + 23 就是带这个标签代表字在哪一行

获取网页源码

接下来要做的,就是通过正常的get请求,去获取评论页的源码,在经过几次的尝试之后,发现这么一个问题,每次请求如果用一个请求头的话,最多你只能拿到30页左右的评论数据,在想继续拿到就会被封锁,即使你传入Cookie值,也无济于事

所以你需要使用一个第三方的库:fake_useragent

pip3 install fake_useragent

使用示例:

from fake_useragent import UserAgent
ua = UserAgent(verify_ssl=False)
ua.random #这里会生成一个随机的浏览器请求头

第二步,在收集网页数据的过程中,每次请求的间隔不要太短,每次请求的过程中还可能触发验证机制,你需要在浏览器端进行手动验证,方可继续使用访问,每次请求评论页的Referer是上一页的网址,意思就是告诉大众点评,你是一页一页评论连续看的,并不是从第一页一直调到其他页,下面放一下源码:

get_data.py

import requests
import time
import re
import sys
from fake_useragent import UserAgent
ua = UserAgent(verify_ssl=False)

url = 'http://www.dianping.com/shop/' + sys.argv[1] + '/review_all'

for i in range(2, int(sys.argv[2])):
    headers = {
        'User-Agent': ua.random,
        'Referer': url + '/p' + str(i-1), 
        'Cookie': sys.argv[3]
    }
    u = url + '/p' + str(i)
    result = requests.get(u, headers=headers).text
    a = re.findall('<p class="not-found-words">抱歉!页面无法访问......</p>', result, re.S)
    b = re.findall("<div class='logo' id='logo'>验证中心</div>", result, re.S)
    if a:
        print('IP被封,死心了吧')
        temp = input()
    elif b:
        print('去浏览器进行手工验证')
        temp = input()
    else:
        with open(str(i)+ '.html', 'w', encoding='utf-8') as file:
            file.write(result)
        print(u + '已经下载完毕')
        time.sleep(5)

在使用get_data.py的时候需要传入三个参数,第一个是你要爬取的店铺的id,在网址中也可以看到,例如这个:1565535039899

第二个参数,是你要爬到第几页

第三个参数,是你从浏览器中复制的Cookie

1565535133342

开始爬取

代码流程如下:

读取网页源码--->从源码中获取CSS文件URL--->从CSS文件中获取到SVG文件的URL--->获取SVG的内容--->选取评论标签--->解析标签SVG的URL--->解析标签的class--->获取坐标--->从SVG中对应字--->保存数据

在这里贴出来几个关键函数:

def get_Word_Point(className, CSSContent):
    point = re.findall(className + '{background:-(.*?).0px.*?-(.*?).0px', CSSContent, re.S)
    x = int(int(point[0][0])/14)
    y = int(point[0][1]) + 23
    return x, y

def get_Word_Content(SVGUrl, x, y, SVG_dic):
    SVGContent = SVG_dic[SVGUrl]
    result = re.findall('<text x="0" y="' + str(y) + '">(.*?)</text>', SVGContent, re.S)
    if result:
        return result[0][x]
    else:
        return get_Word_Content_B(SVGUrl, x, y, SVG_dic)

结果展示

dazhong

1565535819724

Tips

在与大众点评斗志斗勇的过程中,发现大众点评的CSS加密机制是一天两换的,上面的那个加密机制只是白天的一种,如果你访问频率过多,大众点评会自动触发另外一套加密机制,另外一套加密机制将会在下期进行述说,敬请期待。近期,我会把代码整理并开源至Github,欢迎各位的指正与指导!

EmailTyrantLucifer@linuxstudy.cn

个人博客https://www.linuxstudy.cn

最近突然想起来看一看高中时期未看完的小说,心血来潮就去网上找了找资源,发现不论下载下来的txt文件还是专门用来看小说的app不是有广告,就是缺少章节,观看体验十分不爽,为了解决这个问题,自己动手,丰衣足食,于是这个采集脚本应运而生

技术要点:

  • BeautifulSoup4:解析标签
  • Requests:模拟http请求
  • Python3

脚本使用步骤:

  • 安装BeautifulSoup4
pip3 install beautifulsoup4 
  • 安装requests
pip3 install requests
  • 保存以下代码为book.py
import re                                                                             import sys
import requests
import time
from bs4 import BeautifulSoup
from requests.packages.urllib3.exceptions import InsecureRequestWarning 
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

class BookSpider():
    '''爬取顶点小说网小说'''
    def __init__(self):
               
       self.headers = { 
            'Host':'www.dingdiann.com',
            'User-Agent':'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36',
        }
       self.chapter_url_list = list()
       self.chapter_name_list = list()
       self.book_info = dict()

    def get_book_url(self, book_name, author_name):
        '''获取要爬取书籍的详情页'''
        url = 'https://www.dingdiann.com/searchbook.php'
        data = {
            'keyword':book_name
        }
        result = requests.get(url, headers=self.headers, params=data, verify=False).text
        soup = BeautifulSoup(result, 'lxml')
        book_name_list = soup.find_all(name='span', attrs={'class':'s2'})
        book_author_list = soup.find_all(name='span', attrs={'class':'s4'})
        book_name_list.pop(0)
        book_author_list.pop(0)
        for candidate_name in book_name_list:
            book_info_list = list()
            name = str(candidate_name.a.string)
            book_url = str(candidate_name.a.get('href'))
            book_info_tuple = (name, book_url)
            book_info_list.append(book_info_tuple)
            author = str(book_author_list[0].string)
            if author in self.book_info.keys():
                self.book_info[author].append(book_info_tuple)
                book_author_list.pop(0)
            else:
                self.book_info[author] = book_info_list
                book_author_list.pop(0)
        if self.book_info[author_name]:
            for info in self.book_info[author_name]:
                if info[0] == book_name:
                    url = info[1]
                    print('书籍已经找到,您要找的书籍为 ' + book_name + ':' + author_name)   
                    print('3S 后将开始下载~~~')
                    time.sleep(3)
                    return url
        else:
            print('抱歉,书籍未找到,请确认书籍作者及名称是否正确~~~')

    def get_book_info(self, url):
        '''获取书籍的章节列表和地址'''
        all_url = 'https://www.dingdiann.com' + url
        result = requests.get(all_url, headers=self.headers, verify=False).text
        soup = BeautifulSoup(result, 'lxml')
        div = soup.find_all(id='list')[0]
        chapter_list = div.dl.contents

        for text in chapter_list :
            text = str(text)
            content = re.findall('<a href="' + url + '(.*?)" style="">(.*?)</a>.*?', text)
            if content:
                chapter_url = all_url + content[0][0]
                chapter_name = content[0][1]
                self.chapter_url_list.append(chapter_url)
                self.chapter_name_list.append(chapter_name)

        for i in range(12):
            self.chapter_url_list.pop(0)
            self.chapter_name_list.pop(0)
    
    def get_chapter_content(self, name, url):
        '''获取小说每章内容'''        
        try:
            result = requests.get(url, headers=self.headers, verify=False).text
        except:
            print(name + "下载失败~~~")
            return False
        else:
            soup = BeautifulSoup(result, 'lxml')
            div = soup.find_all(id='content')[0]
            div = str(div)
            result = re.findall('<div id="content">(.*?)<script>', div, re.S)[0].strip()
            result = re.sub('<br/>', '\n', result)
            return result
    
    def save_book(self, book_name):
        '''保存小说'''
        for chapter_name in self.chapter_name_list:
            while True:
                chapter_content = self.get_chapter_content(chapter_name, self.chapter_url_list[0])
                if chapter_content:
                    with open(book_name + ".txt", 'a') as f:
                        f.write(chapter_name)
                        f.write("\n")
                        f.write(chapter_content)
                        f.write("\n")
                    self.chapter_url_list.pop(0)
                    print(chapter_name + "已经下载完成")
                    break

    def run(self, book_name, url):
        self.get_book_info(url)
        self.save_book(book_name)

def main(book_name, author_name):
    book = BookSpider()
    url = book.get_book_url(book_name, author_name)
    book.run(book_name, url)

if __name__ == "__main__":
    main(sys.argv[1], sys.argv[2])
  • 使用说明:脚本需要输入两个参数,参数1为小说名称,参数2为作者名称,之后便会将采集到的内容保存在本地,例如:
python3 book.py 天珠变 唐家三少

享用

免责声明

  • 本脚本采集的小说数据来自顶点小说网,只提供数据采集服务,不提供任何贩卖服务
  • 数据采集自https://www.dingdiann.com/,感谢网站管理员的慷慨支持,爬了很多次也没有ban我的ip,希望大家多多支持正版

基本概念

  • 逆波兰式:用比较通俗的语言来讲,就是将多项式的操作数和符号重新排列,操作数在前,符号在后
  • 栈:这种数据结构是一种特殊的线性表,只能在一头进行插入和删除,所以有着先进后出的特点

算法实现

  • 中缀式转逆波兰式

需要一个符号栈,初始为空,遍历多项式的每项,如果是数字,记录进结果,如果是符号,判断当前符号和栈顶符号的优先级(括号 < + - < * /),如果优先级大于栈顶符号,则进栈;如果优先级小于栈顶符号,栈顶出栈,如果出栈的不是括号,记录至结果中,直至栈顶优先级小于当前符号,而后符号进栈,直至遍历完整个表达式,最后将符号栈中的符号出栈记录至结果

  • 计算逆波兰式

需要一个结果栈,初始为空,遍历逆波兰式,如果是数字,进栈,遇到符号,出栈两个操作数,进行计算并将计算结果进栈,直至遍历完成,此时栈顶为表达式结果

代码实现

stack.py

import readline
import time
expression = input("请输入表达式(每个字符用空格隔开):")
expression = expression.split()

def get_Reverse_Polish_Notation(expression):
    '''转化逆波兰式'''
    stack = list()
    result = list()
    symbol = ['(', ')', '+', '-', '*', '/']
    value = {
        '(':1,
        ')':1,
        '+':2,
        '-':2,
        '*':3,
        '/':3,
    }
    for i in expression:
        #如果元素是数字,直接进栈
        if i.isdigit():
            result.append(i)
        #如果元素是符号,进入判断
        if i in symbol:
            #如果栈不为空,则判断符号优先级
            if stack:
                #判断如果符号优先级不高于栈顶符号优先级,则出栈,直至优先级大于栈顶符号
                if value[i] <= value[stack[-1]] and i != '(':
                    #如果符号为右括号,则进行匹配左括号
                    if i == ')':
                        while stack[-1] != '(':
                            result.append(stack.pop())
                        stack.pop()
                    #如果不是右括号,则进行出栈操作,直到大于栈顶符号为止
                    else:
                        while True:
                            result.append(stack.pop())
                            if stack:
                                if value[i] > value[stack[-1]] :
                                    stack.append(i)
                                    break
                            else:
                                    stack.append(i)
                                    break

                #如果大于栈顶符号,直接进栈
                else:
                    stack.append(i)
            #如果初始栈为空,则直接进栈第一个符号
            else:
                stack.append(i)
    #遍历完整个表达式,将栈中剩余的符号出栈
    while stack:
        result.append(stack.pop())
    #返回结果列表
    return result

def count_result(result):
    '''计算逆波兰式'''
    stack = list()
    #遍历逆波兰式,如果是数字,直接进栈,如果是符号,则出栈两个操作数,计算出结果入栈
    for i in result:
        if i.isdigit():
            stack.append(i)
        else:
            if i == '+':
                temp = float(stack[-2]) + float(stack[-1])
                stack.pop()
                stack.pop()
                stack.append(str(temp))
            elif i == '-':
                temp = float(stack[-2]) - float(stack[-1])
                stack.pop()
                stack.pop()
                stack.append(str(temp))
            elif i == '*':
                temp = float(stack[-2]) * float(stack[-1])
                stack.pop()
                stack.pop()
                stack.append(str(temp))
            elif i == '/':
                temp = float(stack[-2]) / float(stack[-1])
                stack.pop()
                stack.pop()
                stack.append(str(temp))
    return float(stack[-1])