pyqt5+feapder+mysql简单爬取newrank的数据。

前言

首先

其实我还想加上pyecharts,但pyechart的参数太灵活,不好配置,而且本身实力不行

,想把爬到的数据生成图,结合pyqt5,算了。不熟悉pyecharts,

其次

网址:

排名列表_日榜 (newrank.cn)

需要简单的js逆向。

最后

我本来想用feapder提供的数据库模块,,算了把,还是用自己封装的数据库,比较习惯。

😛😛😛😛😛

正文

逆向

很简单,打开开发者工具

选择到fetch/xhr那一栏,清空数据。

比如点一下,健康

看一下表单参数,很明显,nonce,xyz,

直接搜索关键词nonce,会找到5个js文件,比较少的,

进去看情况,觉得可能就打个断点,

这个js 文件seajs-config-front.js?t=1673773640133,里面的nounce有15个

第一处就很可疑,打个断点,下面也打个

按f5重新刷新一下。如图nonce=9242e6877',而nonce是i生成的

i=l(),发现这个函数,进去这个函数。

发现是个生成随机数的函数,复制过来,修改一下。

function f() {
    for (var a = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"], b = 0; b < 500; b++)
        for (var c = "", d = 0; d < 9; d++) {
            var e = Math.floor(16 * Math.random());
            c += a[e]
        }
    return c
}

接着继续运行一下,按f11执行下一步代码

突然就会跳到这个函数

看一下上面,很明显的发现很有可能是md5 加密,也有可能不是,把b(a)函数扣下来。

在自己写个md5加密的函数,执行js代码

var md5=require('md5');
var password="/xdnphb/main/v1/day/rank?AppKey=joker&nonce="+f()
console.log(password)
console.log(md5(password))
console.log(b(password)

结果

/xdnphb/main/v1/day/rank?AppKey=joker&nonce=1a8084748
85934b3bce80c9458b9e4c1e4d3b16e9
85934b3bce80c9458b9e4c1e4d3b16e9

二者一样,所以b(a)函数,就是md5函数加密,小写加密

把断点往下移动一点,

其中加密的时候有个参数,第一次我以为,那个参数就是这个a,发生请求。

var a="/xdnphb/main/v1/day/rank?AppKey=joker&nonce="+f();

不行,返回的数据为空,

多次点回复脚本执行,就是那个右上角的三角形,发现正在的参数h是会变的,

但也并不是没有规律,是由end,rank_name,rank_name_group,start,加一个随机数nonce拼接而成的,然后再进行md5加密,得到xyz,把这6个作为参数。作为请求中的表单参数,就可以得到数据

nonce: "53684ad67"
xyz: "87733933b392e9c9196ed6f8f021d86a"

feapder--发送请求

import execjs
# 在pathon中执行js代码需要用到的第三方库
from init import my_sql
# 自己封装的mysql
from init import jm
# 加密的类,封装由md5加密
import feapder
from feapder.setting import DEFAULT_USERAGENT
# DEFAULT_USERAGENT 默认的请求头,不是用的默认请求头,用的是faker生成的
from jsonpath import jsonpath
# 对返回的json进行数据的提取
class spider(feapder.AirSpider):
    def __init__(self,start_time,text,type_text,end_time):
        super().__init__()
        self.start_time=start_time
        self.text=text
        self.type=type_text
        self.end_time=end_time
        self.mysql=my_sql()
# 分别对应需要的参数,
    def start_requests(self):
        yield feapder.Request(url='https://www.newrank.cn/xdnphb/main/v1/day/rank',download_midware=self.download_midware)
# 执行js在的函数,即f()函数,并得到返回值
    def js(self,function_name,*args):
        node = execjs.get()
        ctx = node.compile(open(r'C:\Users\520\PycharmProjects\pythonProject1\爬虫\newrank.js').read())
        result = ctx.call(function_name)
        return result
# 自定义的下载中间件,传参和请求头
    def download_midware(self,request):
        a=self.js('f')
        b=f"/xdnphb/main/v1/day/rank?AppKey=joker&end={self.end_time}&rank_name={self.text}&rank_name_group={self.type}&start={self.start_time}&nonce="+a
        c=jm(b).md_5()
        request.headers={
            'Host': 'www.newrank.cn',
            'user-agent':DEFAULT_USERAGENT,
            'Cookie': 'acw_tc=76b20f6b16748083683148356e6a81b69b0c52718cb76a01d8bf04d0bfaa5f'
        }
        request.data={
            'end': self.end_time,
            'rank_name': self.text,
            'rank_name_group': self.type,
            'start': self.start_time,
            'nonce': a,
            'xyz': c,
        }
        return request
# 解析
    def parse(self, request, response):
        a=response.json
        print(a)
        names = jsonpath(a, '$..name')
        put_nums = jsonpath(a, '$..b')
        text_nums = jsonpath(a, '$..c')
        read_nums = jsonpath(a, '$..d')
        head_report_nums = jsonpath(a, '$..e')
        avg_nums = jsonpath(a, '$..f')
        highest_nums = jsonpath(a, '$..h')
        looking_nums = jsonpath(a, '$..g')
        new_marks = jsonpath(a, '$..log1p_mark')
        ids=1
        self.mysql.use_db('new_rank').clear_table('new_rank')
#有个数据库叫new_rank,里面有个表new_rank,每次都把表清空。链式调用
        for a, b, c, d, e, f, g, h, i in zip(names, put_nums, text_nums, read_nums, head_report_nums, avg_nums, highest_nums, looking_nums, new_marks):
            i = str(round(float(i), 1))
            self.mysql.insert_data('new_rank',ids,a, str(b) + '/' + str(c), d, e, f, g, h, i)
# insert_data,这个函数--往数据库中插入数据
            ids+=1
        self.mysql.close()
# 关闭数据库

pyqt5--可视化选择(选择参数)

比较多

😛😛😛😛😛😛😛

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import time
from init import my_sql
from 爬虫.爬取newrank榜单 import spider
from fenxi import Fen_xi
import datetime
class a(QWidget):
    def __init__(self):
        super().__init__()
        self.init = None
        self.text = None
        self.type_text = None
        self.time_box = None
        self.start_time = None
        self.loser = None
        self.send_request = None
        self.end_time = None
        self.label_4 = None
        self.ok = None
        self.content_box = None
        self.rank = None
        self.label_3 = None
        self.label_2 = None
        self.label = None
        self.month_rank = None
        self.week_rank = None
        self.data_rank = None
        self.type = None
        self.initui()
# 整体初始化
    def initui(self):
        self.resize(777, 539)
        self.setWindowTitle("设置参数")

        self.get_type()
        self.init_content()
        self.set_rank()
        self.init_label()
        self.init_rank()
        self.init_time()
        self.init_ok()
        self.send_init()
        self.init_loser()
# 退出
    def init_loser(self):
        self.loser = QPushButton(self)
        self.loser.setGeometry(240, 360, 93, 28)
        self.loser.setText("退出")
        self.loser.clicked.connect(self.lose)
    def lose(self):
        self.close()
# 初始化发生按钮,重新初始化
    def send_init(self):
        self.send_request= QPushButton(self)
        self.send_request.setText("爬取数据")
        self.send_request.setVisible(False)
        self.send_request.clicked.connect(self.feapder)
        self.send_request.setGeometry(100, 360, 93, 28)
        self.init= QPushButton(self)
        self.init.setText("重新初始化")
        self.init.setVisible(False)
        self.init.clicked.connect(self.init_event)
        self.init.setGeometry(350, 360, 93, 28)
# 重新初始化事件
    def init_event(self):
        self.ok.setVisible(True)
        self.init.setVisible(False)
        self.end_time.setEnabled(True)
        self.content_box.clear()
# 调用feapder写的爬虫发生请求
    def feapder(self):
        a=(self.time_box[0],self.text,self.type_text,self.time_box[1])
        print(a)
        spider(*a).start()
        f=Fen_xi()
        f.show()
        f.exec_()
        self.send_request.setVisible(False)
        self.init.setVisible(True)
# 时间按钮
    def init_time(self):
        self.start_time = QDateTimeEdit(self)
        self.start_time.setDisplayFormat('yyyy-MM-dd')
        self.start_time.setGeometry(220, 260, 221, 22)
        self.start_time.setDateTime(QDateTime.currentDateTime())

        self.end_time = QDateTimeEdit(self)
        self.end_time.setGeometry(220, 310, 221, 22)
        self.end_time.setDisplayFormat('yyyy-MM-dd')
        self.end_time.setDateTime(QDateTime.currentDateTime())
# 确定事件
    def ok_event(self):
        self.type_text = self.type.currentText()
        self.text = self.content_box.currentText()
        rank_text = self.rank.currentText()
        if self.text:
            if rank_text == '日榜':
                self.data_rank.setVisible(True)
                self.start_time.setDateRange(QDate.currentDate().addDays(-7),QDate.currentDate().addDays(-1))
                self.end_time.setDateRange(QDate.currentDate().addDays(-7),QDate.currentDate().addDays(-1))
                self.ok.setVisible(False)

            elif rank_text == '周榜':
                text = '只能选择\n' + self.today(-12) + '\n' + self.today(-19) + '\n' + self.today(-20) + '\n' + '作为开始时间,\n结束时间在开始时间上加6天'
                QMessageBox.information(self, '消息', f'{text}', QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
                self.week_rank.setVisible(True)
                self.start_time.setDateRange(QDate.currentDate().addDays(-26),QDate.currentDate().addDays(-12))
                self.end_time.setDateRange(QDate.currentDate().addDays(-20), QDate.currentDate().addDays(-6))
                self.ok.setVisible(False)

            elif rank_text == '月榜':
                self.month_rank.setVisible(True)
                self.start_time.setDisplayFormat('yyyy-MM-01')
                self.start_time.setDateTime(QDateTime.currentDateTime())
                self.start_time.setDateRange(QDate.currentDate().addMonths(-3),QDate.currentDate().addMonths(-1))
                self.end_time.setEnabled(False)
                self.ok.setVisible(False)
        else:
            QMessageBox.information(self, '消息', '请选择类型', QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
# 确定按钮的初始化
    def init_ok(self):
        self.ok = QPushButton(self)
        self.ok.setText("确定")
        self.ok.clicked.connect(self.ok_event)
        self.ok.setGeometry(360, 110, 93, 28)
# 榜单按钮的初始化
    def init_rank(self):
        font = QFont()
        font.setFamily("新宋体")
        font.setPointSize(14)

        self.data_rank = QPushButton(self)
        self.data_rank.setText("日榜")
        self.data_rank.setVisible(False)
        self.data_rank.setGeometry(200, 180, 93, 28)
        self.data_rank.clicked.connect(self.data_event)
        self.data_rank.setFont(font)

        self.week_rank = QPushButton(self)
        self.week_rank.setText("周榜")
        self.week_rank.setGeometry(330, 180, 93, 28)
        self.week_rank.clicked.connect(self.week_event)
        self.week_rank.setVisible(False)
        self.week_rank.setFont(font)

        self.month_rank = QPushButton(self)
        self.month_rank.setGeometry(470, 180, 93, 28)
        self.month_rank.setText("月榜")
        self.month_rank.clicked.connect(self.month_event)
        self.month_rank.setVisible(False)
        self.month_rank.setFont(font)
# 事件
    def month_event(self):
        start=self.start_time.date().toString('yyyy-MM-01')
        b=start.split('-')
        year=int(b[0])
        month=int(b[1])
        day=int(b[2])
        last_day=self.get_last_day(datetime.date(year,month,day))
        self.month_rank.setVisible(False)
        self.send_request.setVisible(True)
        self.time_box = [str(last_day), start]

    def week_event(self):
        _,start,end=self.get_cha()
        if start==self.today(-12) and end==self.today(-6) or start==self.today(-19) and end==self.today(-13) or start==self.today(-26) and end==self.today(-20):
            self.week_rank.setVisible(False)
            self.send_request.setVisible(True)
            self.time_box=[end,start]
        else:
            QMessageBox.information(self, '消息', '时间选择不正确', QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)

    def data_event(self):
        cha,start,end=self.get_cha()
        if cha>=0:
            self.data_rank.setVisible(False)
            self.send_request.setVisible(True)
            self.time_box=[end,start]

        else:
            QMessageBox.information(self, '消息', '开始时间不能大于结束时间', QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
            return
# 时间
    def today(self,a: int):
        today = datetime.date.today()
        data = today + datetime.timedelta(days=a)
        return str(data)
    def get_last_day(self,day):
        next_month = day.replace(day=28) + datetime.timedelta(days=4)
        return next_month - datetime.timedelta(days=next_month.day)
    def get_cha(self):
        start=self.start_time.date().toString('yyyy-MM-dd')
        end=self.end_time.date().toString('yyyy-MM-dd')
        starts= time.strptime(start, '%Y-%m-%d')
        t1 = time.mktime(starts)
        ends = time.strptime(end, '%Y-%m-%d')
        t2 = time.mktime(ends)
        cha=int(t2-t1)
        return cha,start,end
# 标签初始化
    def init_label(self):
        self.label = QLabel(self)
        self.label.setText("类型")
        self.label.setGeometry(160, 40, 72, 15)

        self.label_2 = QLabel(self)
        self.label_2.setText("开始时间")
        self.label_2.setGeometry(100, 260, 72, 15)

        self.label_3 = QLabel(self)
        self.label_3.setText("结束时间")
        self.label_3.setGeometry(100, 310, 72, 15)

        self.label_4 = QLabel(self)
        self.label_4.setText("榜单")
        self.label_4.setGeometry(160, 110, 72, 15)

    def set_rank(self):
        self.rank = QComboBox(self)
        self.rank.addItems(['日榜', '周榜', '月榜'])
        self.rank.setGeometry(230, 110, 87, 22)

    def init_content(self):
        self.content_box = QComboBox(self)
        self.content_box.setGeometry(360, 40, 87, 22)

    def get_type(self):
        self.type = QComboBox(self)
        self.type.setGeometry(240, 40, 87, 22)
        self.type.addItems(['请下拉', '生活', '资讯'])
        self.type.activated.connect(self.type_choice)

    def type_choice(self):
        text = self.type.currentText()
        if text == '请下拉':
            self.type.removeItem(0)
        else:
            if text == '生活':
                self.content_box.clear()
                self.content_box.addItems(['文化', '百科', '健康', '时尚', '美食', '乐活', '旅行', '幽默', '情感', '体娱', '美体', '文摘'])
            elif text == '资讯':
                self.content_box.clear()
                self.content_box.addItems(['民生', '财富', '科技', '创业', '汽车', '楼市', '职场', '教育', '学术', '企业'])


if __name__ == '__main__':
    app = QApplication(sys.argv)
    app.setStyle(QStyleFactory.create('Fusion'))
    a = a()
    a.show()
    sys.exit(app.exec_())

我都不知道怎么说了,要说的过多了,一言难尽,运行一下

操作

开始

pycharm中按ctrl+shift+f10,开始运行

出现如下界面

类型

先选择类型,

选择了--生活,在旁边的下拉框会出现相应的选项,如果修改,旁边的也会修改

不选直接点确定,会出现一个提示框

选择了类型后,

榜单

下面是选择榜单,如果选择日榜,

点击确定按钮,

此时,确定按钮会消失,会出现相应的榜单按钮,看

如果选择周榜,点击确定

会弹出一个时间提示框

对时间的要求

选择月榜,结束时间就不能用,开始时间也只能选择以前三个月内的

日期

我为什么这么设置?是基于参数的考虑,选择的榜单不同,选择的日期不同,范围也不同。

如果选择的是日榜,时间范围只能今天的前7天,今天不能算,而且选择是开始日期不能大于结束日期

(感觉说日期更准确,不应该说时间,我的)

周榜:日期的间隔必须为7,开始日期也是有所规定的,是今天的前12天,前19天,前16天。

月榜:每月的开始到每月的结束,不同月份,结果不一样

榜单按钮

比如是日榜,点击之后,日榜就会消失,出现你一个按钮 --爬取数据

此时如果再修改数据,没有用,数据的修改需要按钮,可以修改,但无法真正的修改,按钮消失了。

爬取数据

点击爬取按钮,爬取按钮会消失,会出现重新初始化按钮,如果没报错

先会出现一个对话框

这个对话框,如果点击显示数据,就会把刚才爬到数据展示出来

当然这个对话框并没有完成,上面有个--图的选择,我希望把数据转换成图,用pyecharts,其他的,matplotlib不是很好。但还没完成,有待完善。慢慢来。

退出对话框

退出之后,主页面就变成这样

要么退出,要么,重新初始化,如果点重新初始化按钮,重新初始化按钮就会消失,那么最开始的确定按钮就会出现。又恢复原来的样子,继续选择,继续爬取。

补充

封装的mysql

# 就给出用过的,太多了
    def __init__(self):
        self.connect= pymysql.connect(host=host, port=port, user=user,password=password,charset='utf8')
        self.cursor=self.connect.cursor()
    def sql(self,a:str):
        self.cursor.execute(a)
        self.commit()
    def use_db(self, name):
        self.sql(f'use {name};')
        return self
    def clear_table(self,table_name):
        a=f'truncate table {table_name};'
        self.sql(a)
        return self
    def desc_tablename(self, tablename, show=True):
        a = f'desc {tablename};'
        b = self.sql_all(a)
        c = []
        self.tb.field_names = ['列名', '类型', '空', '约束', '默认值', '额外信息']
        for i in b:
            column_name = i[0]
            column_type = i[1]
            null = i[2]
            key = i[3]
            default = i[4]
            extra = i[5]
            self.tb.add_row([column_name, column_type, null, key, default, extra])
            c.append(column_name)
        if show:
            print(self.tb)
            self.clear_tb()
            return c, self
        else:
            self.clear_tb()
            return c, self
    def insert_data(self, table_name, *args):
        arg = self.desc_tablename(tablename=table_name,show=False)[0]
        arg=','.join(arg)
        c = str(args)
        a = f'insert into {table_name}({arg}) values ' + c
        d = a + ';'
        self.sql(d)
        return self
# 随便写个类。

对话框

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from init import my_sql

class Fen_xi(QDialog):
    def __init__(self):
        super().__init__()
        self.comboBox = None
        self.display = None
        self.ok = None
        self.label = None
        self.loser = None
        self.table = None
        self.initui()

    def initui(self):
        self.resize(1487, 708)
        self.setWindowTitle("展示")



        self.loser = QPushButton(self)
        self.loser.setText("退出")
        self.loser.clicked.connect(self.lose)
        self.loser.setGeometry(600, 40, 93, 28)

        self.label = QLabel(self)
        self.label.setText("  图的选择:")
        self.label.setGeometry(340, 10, 91, 16)

        self.comboBox = QComboBox(self)
        self.comboBox.setGeometry(360, 40, 87, 22)
        self.comboBox.addItems([])

        self.ok = QPushButton(self)
        self.ok.setText("确定")
        self.ok.setGeometry(490, 40, 93, 28)

        self.display = QPushButton(self)
        self.display.setText("显示数据")
        self.display.clicked.connect(self.init_table)
        self.display.setGeometry(490, 80, 93, 28)
    def lose(self):
        self.close()
    def init_table(self):
        a=my_sql().use_db('new_rank')
        data,y=a.select('new_rank')
        header = y.desc_tablename('new_rank',show=False)[0]
        y.close()
        table = QTableWidget(0, len(header),self)
        table.show()
        table.setGeometry(145, 120, 1250, 531)

        for i, t in enumerate(header):
            item = QTableWidgetItem()
            item.setText(f'{t}')
            table.setHorizontalHeaderItem(i, item)
            table.setColumnWidth(i, 130)

        row_count = table.rowCount()
        for i in data:
            table.insertRow(row_count)
            for ids, t in enumerate(i):
                cell = QTableWidgetItem(str(t))
                table.setItem(row_count, ids, cell)
            row_count += 1

总结

按钮之间的出现与消失,对参数进行选取,好玩

😆😆😆😆😆😆😆😆😆😆

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值