Pythonはバスの目覚まし時計を実装しています-無駄にバスを待つ必要はもうありません

人を待つ、夕食を待つ、バスを待つなど、人生には面倒なことがたくさんあります。待ちきれないことが多いです。慣れているのに、どうしたらいいかをいつも考えています。それをより便利で効率的にします。今日、私たちはWindowsサービスを使用してバスの目覚まし時計を実装し、乗り心地をよりエレガントで意味のないものにし、先に進んでいます。

問題と分析

明日はバスで通勤します。とても便利ですが、バスのそばを通り過ぎることがよくあります。振り返らずに主人が去るのを見ると、気分がとても複雑(狂気)です。どうすればバスに乗り遅れないのでしょうか?、長く待たされることはありません。運から見積もりまで、たくさんの方法を見つけました。その後、リアルタイムのバス情報を照会できるアプリやWebサイトをたくさん見つけましたが、開くたびにルートを選択します。 、駅を選んで問い合わせてください。とても面倒で、注意を払い続ける必要があり、注意を怠ると見逃してしまいます。

リアルタイムのバス情報はウェブサイトから照会できるので、クローラーを使用して支援できますか?問題ないはずですので、プログラムを継続して実行し、リマインダーの時間を設定します。たとえば、出勤時や出勤時のバスが駅から遠くない場合は、出勤を忘れないようにしましょう。気持ちいいです。 。

言語として強力なPythonを使用します。起動を忘れないようにするには、サービスにするのが最善です。Linuxが最も便利ですが、Linuxホストが必要です。私は通常Windowsを事務作業に使用しているため、それをWindowsサービスにすることを選択しました。また、スケジュールされたタスクで実行することもできますが、リマインダーの期間を設定するのに十分な柔軟性がありません。

計画を決定し、行動を開始します

練習

シンプルなアイデアだけがあり、シンプルなプロジェクトはありません。時間のプロセスは、到着時間の取得、通知の送信、サービスの作成、改善のいくつかの部分に分かれています。

到着時間を取得する

多くの都市には、リアルタイムの公共交通機関クエリWebサイトがあります。たとえば、北京の北京公共交通機関グループのWebサイトhttp://www.bjbus.comでは、リアルタイムの公共交通機関情報をクエリし、ルート、フォームの方向、搭乗駅を選択できます。 、次にリアルタイムの公共交通機関情報を取得します。

ブラウザでF12をクリックし、[ネットワーク]タブを開き、[ネットワークでクエリ]をクリックして、クエリ要求を見つけます。

クエリリクエスト

GETリクエストを確認できます。URLは次のとおりです。http://www.bjbus.com/home/ajax_rtbus_data.php?act = busTime&selBLine = 1&selBDir = 5276138694316562750&selBStop = 2

リクエストパラメータの意味は次のとおりです。

  • 行為:クエリタイプ、固定値はbusTimeです
  • selBLine:ライン。ライン名のみを表します。たとえば、1は1台の車を意味します。
  • selBDir:運転方向、値はより複雑であり、実際のクエリを通じて取得する必要があります
  • selBStop:搭乗駅。値は正式な方向の路線のシリアル番号で、1から始まります。たとえば、2は2番目の駅を意味します。

httpxを使用して、テストします

httpxは、従来のライブラリリクエストに基づいて実装され、インターフェイスはより簡潔で効率的であり、pip installhttpxによってインストールされます。

Python環境で、を実行します

>>> import httpx
>>> url = "http://www.bjbus.com/home/ajax_rtbus_data.php?act=busTime&selBLine=1&selBDir=5276138694316562750&selBStop=2"
>>> r = httpx.get(url)
>>> print(r.status_code)
200
>>> print(r.text)
'{"html":"<div class=\\"inquiry_header\\"><div class=\\"left fixed\\"> ...

httpx.getはGETリクエストを送信し、レスポンスオブジェクトを返すことができます。status_codeはリクエストステータスコード、textはレスポンスコンテンツです。

ご覧のとおり、返されるデータはJSON形式です。httpx応答オブジェクトのjsonメソッドを使用すると、結果をPython辞書オブジェクトに変換することができます。

>>> ret.json()
{'html': '<div class="inquiry_header"><div class="left fixed"><h3 id="lh">1路</h3>< ...

返された結果を分析すると、最初に、次のような公共交通機関のより詳細なリアルタイム情報があることがわかります。

<p>最近一辆车距离此还有 3 站, <span>589</span> 米,预计到站时间 <span>1</span> 分钟</p>

車両情報がない場合:

<p>车辆均已过站</p>

したがって、到着予定時刻を抽出するだけです。

BeautifulSoupを使用してhtmlを解析し、到着時間を取得します。

import httpx
from bs4 import BeautifulSoup as bs4

url = "http://www.bjbus.com/home/ajax_rtbus_data.php?act=busTime&selBLine=1&selBDir=5276138694316562750&selBStop=2"
r = httpx.get(url).json()
b = bs4(r.get('html'), 'html.parser')
info = b.find('article')
i = info.find_all('p')[1]
ret = re.search(r'\d+(?=\s分钟)', i.text)

BeautifulSoupはpipinstallbeautifulsoup4でインストールできます

  • BeautifulSoupライブラリを導入し、エイリアスbs4を付けます
  • 要求応答を取得し、それを辞書オブジェクトに変換します
  • Pythonに付属のhtml.parserパーサーを使用して、辞書でhtml属性を抽出し、BeautifulSoupオブジェクトbに変換します。他のパーサーをインストールする必要がある場合があります。
  • HTMLコンテンツを分析することで、有効な情報が記事タグに含まれていることがわかり、findを使用して記事タグのみを含むBeautifulSoupオブジェクト情報を取得できます。
  • infoでpタグを抽出します。ここで、2番目の要素(リストの最初の要素のインデックスは0)は、抽出する必要のあるコンテンツであり、それをiに配置します。
  • iのテキストから、正規表現を使用して車両の到着分数を抽出します。正規表現の意味は、1つ以上の数字に続けてスペースと文字分を一致させることです。
  • 一致する場合は、車両が到着した分数を返し、Noneを返します。これは、車両がまだ出発していないことを示します。

上記の方法は、実際には単純なクローラーであり、適切な時間が見つかり、リマインダーが発行され、コアタスクが完了するまで繰り返し実行されます。


多くの人がPythonを学び、どこから始めればよいのかわかりません。
多くの人がPythonを学び、基本的な文法を習得した後、どこから始めればよいかわかりません。
事例研究を行った多くの人々は、より高度な知識を学ぶ方法を知りません。
したがって、これら3つのタイプの人々のために、ビデオチュートリアル、電子書籍、およびコースのソースコードを無料で受け取ることができる優れた学習プラットフォームを提供します。
QQグループ:721195303


 

通知を送信する

ポップアップウィンドウやWindowsでのメッセージなど、通知を送信する方法はたくさんありますが、実際には多くの困難があります。そのため、メール通知が選択されます。これは、実装が簡単なだけではありません。メールクライアントがで構成されている場合携帯電話、メールが送信されます。リマインダー、より便利

Pythonでメールを送信するのは簡単です。コードを見てください。

import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

msg = MIMEMultipart('alternative')  # 实例化email对象
msg['from'] = '[email protected]'  # 对应发件人邮箱昵称、发件人邮箱账号
msg['to'] = ';'.join(['[email protected]'])  # 对应收件人邮箱昵称、收件人邮箱账号
msg['subject'] = '你好'  # 邮件的主题
msg.attach(MIMEText('你好,很高兴认识你...', 'html'))  # 附加正文

SMTP_SERVER = 'smtp.example.com'  # 邮箱服务器
SSL_PORT = '465'  # 端口
USER_NAME = 'username'  # 邮箱用户名
USER_PWD = 'password'  # 密码

smtp = smtplib.SMTP_SSL(SMTP_SERVER, SSL_PORT)  # 邮件服务器地址和端口
smtp.ehlo()  # 用户认证
smtp.login(USER_NAME, USER_PWD)  # 括号中对应的是发件人邮箱账号、邮箱密码
smtp.sendmail(FROM_MAIL, TO_MAIL, str(msg))  # 收件人邮箱账号、发送邮件
smtp.quit()  # 等同 smtp.close()  ,关闭连接

たとえば、私が受け取った電子メール通知は次のとおりです。

メールアラート

生産サービス

すべての準備が整いました。風のおかげです。次に、スクリプトを実行可能プログラムにして、Windowsサービスとして登録する必要があります。

Windowsサービススクリプト

Pythonで書くには、win32apiライブラリの助けが必要です

win32apiライブラリをインストールします

pip install pywin32

これはサービススクリプトフレームワークです。

import win32api
import win32event
import win32service
import win32serviceutil
import servicemanager

class MyService(win32serviceutil.ServiceFramework):
    _svc_name_ = "服务名称"
    _svc_display_name_ = "在服务列表中显示的名称"
    _svc_description_ = "服务描述"
    def __init__(self, args):
      win32serviceutil.ServiceFramework.__init__(self, args)
      self.stop_event = win32event.CreateEvent(None, 0, 0, None)

    def SvcDoRun(self):
      self.ReportServiceStatus(win32service.SERVICE_START_PENDING)
      # 这里写服务启动后的业务逻辑
      win32event.WaitForSingleObject(self.stop_event, win32event.INFINITE)

    def SvcStop(self):
      self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
      # 这里写服务即将停止时的业务逻辑
      win32event.SetEvent(self.stop_event)
      self.ReportServiceStatus(win32service.SERVICE_STOPPED)

if __name__ == "__main__":
  if len(sys.argv) == 1:
    servicemanager.Initialize()
    servicemanager.PrepareToHostSingle(MyService)
    servicemanager.StartServiceCtrlDispatcher()
  else:
    win32serviceutil.HandleCommandLine(MyService)
  • サービス関連パッケージの紹介
  • win32serviceutil.ServiceFrameworkから継承されたサービスクラスを定義します
  • _svc_name _、_ svc_display_name _、_ svc_description_はサービス宣言属性です
  • サービスクラス初期化__init__メソッドでは、サービス停止イベントが定義され、実際のアプリケーションでビジネス関連の属性を初期化できます。
  • SvcDoRunは、サービス開始時のコールバックメソッドであり、サービスの実行時に処理ロジックを記述できます。
  • SvcStopは、サービス終了時のコールバックメソッドであり、サービス終了時に処理ロジックを記述できます。
  • ReportServiceStatusは、サービスステータスの通知方法であるため、サービスマネージャはサービスステータスを時間内に取得できます。
  • スクリプト実行時にパラメータがない場合はサービス開始を意味し、パラメータがある場合はインストール(インストール)、開始(開始)などのサービス管理方法を実行します。

ベール

サービスコードを記述したら、EXEとしてパッケージ化する必要があります

上記のサービススクリプトは、EXEとしてパッケージ化せずにサービスとして登録できますが、環境やコンポーネントの参照の問題により、登録されたサービスが失敗することがよくあります。

Pyinstallerツールは、PythonスクリプトをWindows実行可能ファイルにパッケージ化できます

インストール:

pip install pyinstaller

次に、コマンドラインで直接使用できます。たとえば、パッケージservice.pyをEXEとして使用できます。

pyinstaller service.py

パッケージング処理が遅く、大量の情報が出力されます。エラーメッセージが表示されなければ、パッケージングは​​成功です。

パッケージ化後、足跡が配置されているディレクトリにbuildディレクトリとdistディレクトリが作成されます。distディレクトリには、スクリプト名と同じ名前のパッケージ化されたEXEがあります。

注:パッケージ化されたプログラムがサービスに登録された後、起動時にwin32timezoneが見つからないというエラーが報告される場合があります。このとき、パラメーターを追加する必要があります。--hiddenimport win32timezonepackagesコマンドは次のように置き換えられます。pyinstaller- hiddenimport win32timezone -Fservice.pyパックするだけです

登録サービス

実行可能ファイルの準備ができたら、サービスとして登録できます

まず、管理者権限でコマンドラインを実行する必要があります

管理者としてコマンドラインを実行する

  • 登録サービスservice.exeのインストール
    登録、コンピューター管理サービスまたはタスク管理サービスリストから、名前はスクリプトのサービスクラス_svc_display_name_で定義された名前であることがわかります。
  • サービスの開始service.exestart
    は、サービスリストでも開始できます。
  • サービスの停止service.exeの
    停止は、サービスリストでも停止できます。
  • サービスの登録を解除するservice.exeremoveサービスの登録を解除するとき
    は、最初にサービスを停止する必要があります。そうしないと、サービスリストにサービス本体が含まれます。

インストールパラメータと開始パラメータを使用してサービスを管理するだけでなく、Windowsコマンドscを使用して操作することもできます。興味がある場合は、それについて学ぶことができます。

サービスを開始してエラーを報告した場合は、Windowsイベントマネージャーでエラーログを表示して、詳細情報を取得できます。

 

完璧

公共交通機関向けのリアルタイム情報クローラーの構築からWindowsサービスの開始まで、主要な作業は完了しました。全体として実行した後、少なくともソリューションが実行可能であることを確認できます。

それが実際に適用されることになっている場合、対処する必要がある多くの詳細がまだあります

リマインダー期間を設定する

Windowsサービスとして実行する場合、長時間実行されます。バスリマインダー機能は特定の期間のみ有効である必要があるため、現在の時刻がリマインダー時間ウィンドウに入るかどうかを判断する必要があります。 onTimeメソッドはこれを行うことができます:

import datetime
def onTime(begin, end):
  d_time = datetime.datetime.strptime(
    str(datetime.datetime.now().date())+begin, '%Y-%m-%d%H:%M')
  d_time1 = datetime.datetime.strptime(
    str(datetime.datetime.now().date())+end, '%Y-%m-%d%H:%M')
  n_time = datetime.datetime.now()
  if n_time > d_time and n_time < d_time1:
    return True
  else:
    return False
  • 日時パッケージをご紹介します
  • このメソッドは、「18:00」、「18:30」など、開始時間と終了時間の2つのパラメーターを受け入れます。
  • 現在の期間が開始時刻と終了時刻の範囲内にある場合はTrueを返し、そうでない場合はFalseを返します。

サービスの起動メソッドで、ループを記述し、毎回現在の時刻を判断します。onTimeがTrueを返すと、リマインダービジネスコードが入力されます。

複数行をサポート

同じロードカーですが、異なる方向は異なるルートと見なされる必要があるため、リマインダーメソッドの実行では、複数のルートを同時に判断する必要があります。

def run(self):
  for line in self.config.lines:
    if self.onTime(line['begin'], line['end']):
      if line.get('needSentMail', True):
        bustime = self.getBusTime(line)
        if  bustime is not None:
          if int(bustime) <= int(line.get('latestLeaveMinute', self.config.latestLeaveMinute)):
            self.mailClient.send_mail(self.config.alertMail, '班车提醒: '+line['line'], '车辆即将到站,现在出发正当时')
            line['needSentMail'] = False  # 发送通知后,不必再发了
    else:
        line['needSentMail'] = True

その中で、needSentMailは、通知を送信する必要があるかどうかを示します。通知が時間枠で送信された場合、再度送信する必要はありません。時間枠が経過した場合は、送信するように設定する必要があります。

行はリストとして構成されます。

lines = [{
  "line": "835快",
  "dir": "5066222788346588777",
  "stop": "13",
  "begin": "08:00",
  "end": "08:30"
}, {
  "line": "835快",
  "dir": "4997908670784162973",
  "stop": "3",
  "begin": "19:00",
  "end": "20:30"
}]

構成

コードにビジネス関連の情報を書き込むことはお勧めできません。そのため、通知メールの構成や回線情報などを構成に書き込む必要があります。これにより、ビジネスが変更された場合に変更するだけで済みます。構成ファイル。json形式の構成ファイルconfig.jsonを使用します。

{
  "loopWaitSeconds": 60,
  "spurtWaitSeconds": 10,
  "latestLeaveMinute": 5,
  "mailConfig": {
    "FROM": "[email protected]",
    "HOST": "smtp.example.com",
    "PORT": "465",
    "USER": "tom",
    "PASS": "password",
    "SSL": true
  },
  "alertMail": "[email protected]",
  "lines": [{
    "line": "835快",
    "dir": "5066222788346588777",
    "stop": "13",
    "begin": "08:00",
    "end": "08:30"
  }, {
    "line": "835快",
    "dir": "4997908670784162973",
    "stop": "3",
    "begin": "19:00",
    "end": "20:30"
  }]
}

構成フィールドは比較的単純なので、次のことを説明する必要があります。

  • loopWaitSeconds:空のループで待機する秒数
  • spurtWaitSeconds:リマインダー時間ウィンドウに入るのを待つ秒数
  • latestLeaveMinute:出発できる時間、つまり車両が到着するまでの時間、通知を送信する必要があります

Pythonは、json構成ファイルを簡単に読み取ることができます。読み取った後、クラスに変換します。これは、コードでの使用に便利です。


class Config:
  def __init__(self, config):
    self.loopWaitSeconds = config.get("loopWaitSeconds", 60)
    self.spurtWaitSeconds = config.get("spurtWaitSeconds", 10)
    self.mailConfig = config.get("mailConfig", None)
    self.latestLeaveMinute = config.get("latestLeaveTime", 5)
    self.lines = config.get("lines", {})

import json

with open(r"C:\config.json", "r", encoding='UTF-8') as config_file:
  config = Config(json.load(config_file))  # 整体配置

ここで、構成ファイルの場所は、プログラムがサービスとして実行される場合、現在のパスが一時ディレクトリであるため、絶対パスを書き込む方が便利であることに注意してください。

総括する

それは問題を解決しますが、多くのアプリと同じくらいエレガントになるには多くの作業が必要です。この練習を通じて、Pythonパッケージング、Windowsサービス、単純なクローラー、電子メール送信などの機能を理解し、将来的に他のアプリケーションの基礎を築くことができます。たとえば、このフレームワークに基づいて、チェックを行うことができます。自分自身をより自由で
快適にするための機能です。怠惰な行動を取りましょう。行動によってもたらされる驚きは、瞬間的な安らぎをはるかに上回ります...
読んでいただきありがとうございますコード例には比較的完全なコードがあります。参照してください。と研究

自分で作成したPython学習グループ:721195303を引き続きお勧めします。全員がPythonを学習しています。Pythonを学習したい、または学習している場合は、ぜひ参加してください。誰もがソフトウェア開発パーティーであり、最新のPythonの高度な資料のコピーや、2021年に私が編集したゼロベースの教育など、随時(Pythonソフトウェア開発に関連するもののみ)。高度でPythonに興味のある友人を歓迎します。

 

おすすめ

転載: blog.csdn.net/pyjishu/article/details/114625591