Python爬取12306车票信息进阶日志版

闭包

python中最强大的工具——装饰器,咱们小白必须学学咋用

正好咱们来写个入门级的装饰器——日志,用到上一个爬12306车票的小爬虫上

先简单介绍一下装饰器吧,简单地说:他们是修改其他函数的功能的函数。他们有助于让我们的代码更简短,也更Python范儿。其实装饰器也是闭包,之前有一次面试被问到闭包是什么,结果一脸懵,唉,只能怪自己当初也没准备好就去送菜。

回归正题,咱们先写一个闭包,闭包也就是函数中定义一个内函数,我们称包含内函数的函数为外函数,这个内函数调用了外函数的变量,并且外函数返回内函数。听起来可能晦涩难懂,那咱就跑个闭包看一看:

def A(x):
    def B():
        print(x)
    return B
demo=A(7)
demo()

A就是外函数了,B是内函数,大家都知道一个函数运行结束后会清除所有变量来腾出内存,但这个闭包的外函数A在结束时会检测到内函数B调用了变量x,A便会在结束前把变量x的值绑定给内函数b。可以自己多写几个闭包试试,有助于后面写装饰器。

对于demo=A(7)后怎么可以直接demo(),小白会疑惑demo怎么变成函数了,在python中万物皆对象,函数也是对象,执行demo=A(7),函数A返回了B函数的名称,那么demo此时就等同于B,即demo()可以看作为B(),要是还不懂可以自己多搜索一下相关内容。

装饰器

现在开始写装饰器。上代码

def logit(func):
    def with_logging(*args,**kwargs):
        print(func.__name__+" was called")
        return func(*args,**kwargs)
    return with_logging
    
def addition(x):
    return x+x
    
addition = logit(addition)
addition(2)

仔细一看就知道,logit()是一个日志装饰器的雏形,args和kwargs是万能参数,因为一个装饰器通常会装饰很多不同的函数或方法,而这些函数或方法会有不同数量的参数,所以直接使用万能参数,args会把参数转为tuple,kwargs会把带键值的参数转为dict,具体内容这里就不说了。运行结果如下
在这里插入图片描述

现在可以知道装饰器就是在被装饰的函数或方法被调用时之前运行,然后被调用的函数或方法正常运行。现在很容易可以联想到装饰器的两个用途,日志和授权认证。

刚刚调用装饰器的方式是addition = logit(addition),其实还可以直接通过在被装饰函数定义的上一行使用@装饰器来进行装饰,如下

def logit(func):

    def with_logging(*args,**kwargs):
        print(func.__name__+" was called")
        return func(*args,**kwargs)
    return with_logging

@logit
def addition(x):
    return x+x

addition(2)

运行结果:
在这里插入图片描述
现在装饰器还有一个小问题,如下图所示
在这里插入图片描述
这并不是我们想要的!Ouput输出应该是"addition"。这里的函数被with_logging替代了。它重写了我们函数的名字和注释文档(docstring)。幸运的是Python提供给我们一个简单的函数来解决这个问题,那就是functools.wraps。我们修改上一个例子来使用functools.wraps:
在这里插入图片描述
现在好多了。我们直接把日志装饰器用到上一个12306车票小爬虫上吧

实际运用

如果不清楚这个小爬虫怎么写,可以查看我的上一篇文章(虽然写的很一般/(ㄒoㄒ)/~~)

Python爬取12306车票信息(点击此处查看)

下面是加入日志装饰器后的小爬虫,顺便优化了一下解析函数,不然解析函数会被调用很多次,然后日志里全是解析函数被调用的记录。。。。。

stations.py

import re
import requests
 
url = 'https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9161'
requests.packages.urllib3.disable_warnings()
response = requests.get(url, verify=False)
stations = re.findall(u'([\u4e00-\u9fa5]+)\|([A-Z]+)', response.text)
stations=dict(stations)

tickets.py

from stations import stations
from prettytable import PrettyTable
from selenium import webdriver
from functools import wraps
import datetime
import requests
import json
import re
import time
header = ["车次","出发站","到达站","出发时间","到达时间","历时","商务座","一等座","二等座","高级软卧","软卧","动卧","硬卧","软座","硬座","无座"]
class down_tickets(object):

    def loggit(func):
        @wraps(func)
        def notes(*args):
            now=datetime.datetime.now().strftime('%Y-%m-%d,%H:%M:%S')
            sResult = now+" 方法 "+func.__name__+" 被调用\n"
            with open("C:\\Users\\wzx12\\Desktop\\reptitle\\log.txt", "a", encoding='utf-8') as f:
                f.write(str(sResult))
                f.close()
            return func(*args)
        return notes

    @loggit
    def get_tickets(self,froms,tos,date):
        froms = stations[froms]
        tos = stations[tos]
        chrome_options = webdriver.ChromeOptions()
        chrome_options.add_argument('--headless')
        browser = webdriver.Chrome(chrome_options=chrome_options)
        print("请稍等,此处可能需要花费几秒钟.....")
        browser.get('https://www.12306.cn/index/index.html')
        time.sleep(3)
        Cookie = browser.get_cookies()
        strr = ''
        for c in Cookie:
            strr += c['name']
            strr += '='
            strr += c['value']
            strr += ';'
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36',
            'Cookie':strr
            }
        print(headers)
        browser.quit()
        request_url = 'https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date={}&leftTicketDTO.from_station={}&leftTicketDTO.to_station={}&purpose_codes=ADULT'.format(date,froms,tos)
        response = json.loads(requests.get(request_url,headers=headers).text)
        result = response['data']['result']
        new_list = []
        for item in result:
            if not '列车停运' in item:
                new_list.append(item)
            else:
                pass
        return new_list

    @loggit
    def get_info(self):
        fw = input("请输入出发地>>>")
        tw = input("请输入目的地>>>")
        st = input("请输入出发时间;格式:(年-月-日)(默认为今日日期)>>>>")
        if st == '':
            st = datetime.date.today()
            return fw,tw,st
        else:
            today = datetime.date.today()
            date = str(today).split('-')
            list = st.split('-')
            if int(list[0]) < int(date[0]) or int(list[0]) > int(date[0]):
                exit("输入的年份不在我的查询范围之内")
            else:
                if int(list[1]) < int(date[1]) or int(list[1]) > int(date[1])+1:
                    exit("你输入的月份不在我的查询范围之内")
                else:
                    if int(list[2]) < int(date[2]):
                        exit("你输入的日期不在我的查询范围之内")
                    else:
                        if int(list[1]) < 10 and int(list[1][0]) != 0:
                            list[1] = '0' + list[1]
                        if int(list[2]) < 10 and int(list[2][0]) != 0:
                            list[2] = '0' + list[2]
                        return fw,tw,list[0] + '-' + list[1] + '-' + list[2]
    @loggit
    def decrypt(self,string,fu):
        new_dict = {v: k for k, v in stations.items()}
        for item in string:
            item = ''.join(item)
            reg = re.compile('.*?\|预订\|.*?\|(.*?)\|.*?\|.*?\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|.*?\|.*?\|.*?\|.*?\|.*?\|.*?\|.*?\|.*?\|.*?\|.*?\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|.*?\|.*?\|.*?\|.*')
            result = list(re.findall(reg,item)[0])
            result[1] = new_dict[result[1]]
            result[2] = new_dict[result[2]]
            results = [result[0],result[1],result[2],result[3],result[4],result[5],result[-1],result[-2],result[-3],result[-12],result[-10],result[-6],result[-5],result[-8],result[-4],result[-7]]
            fu.add_row(results)
        return fu

if __name__ == '__main__':
    pt = PrettyTable()
    pt.field_names = header
    t = down_tickets()
    [fw,tw,st]=t.get_info()
    trainlist=t.get_tickets(fw,tw,st)
    pt = t.decrypt(trainlist,pt)
    print(pt)

小白的文章,要是有啥缺陷请各位大佬多指点指点。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值