3種類のCSVを読み込んで1つのJSONデータにするpythonをChatGPTに作成してもらった

以下の3つのファイルを1つのJSONデータにするPythonプログラムをChatGPTに作成してもらいました。

mail-src,mail-subject
aaa@yyy.com,"test ay1"
aaa@yyy.com,"test ay2"
aaa@zzz.com,"test az1"
bbb@yyy.com,"test by1"
bbb@zzz.com,"test bz1"
ccc@yyy.com,"test cy1"
ccc@zzz.com,"test cz1"

uid,mail-src
user-a,"aaa@yyy.com,aaa@zzz.com"
user-b,"bbb@yyy.com,bbb@zzz.com"
user-c,"ccc@yyy.com,ccc@zzz.com"

dest,state,category,black,pc,uid,mail
https://aaa.com:9001/aaa,pass,phishing,no,pc-a,user-a,aaa@xxx.com
https://bbb.com:9001/bbb,block,c2,yes,pc-b,user-b,bbb@xxx.com
https://ccc.com:9001/ccc,pass,c2,yes,pc-c,user-c,ccc@xxx.com

出来上がりのJSONイメージは以下の通りです。

[
  {
    "dest": "https://aaa.com:9001/aaa",
    "state": "pass",
    "category": "phishing",
    "black": "no",
    "pc": "pc-a",
    "uid": "user-a",
    "mail": "aaa@xxx.com",
    "mail-src": {
      "aaa@yyy.com": [
        "test ay1",
        "test ay2"
      ],
      "aaa@zzz.com": [
        "test az1"
      ]
    }
  },
  {
    "dest": "https://bbb.com:9001/bbb",
    "state": "block",
    "category": "c2",
    "black": "yes",
    "pc": "pc-b",
    "uid": "user-b",
    "mail": "bbb@xxx.com",
    "mail-src": {
      "bbb@yyy.com": [
        "test by1"
      ],
      "bbb@zzz.com": [
        "test bz1"
      ]
    }
  },
  {
    "dest": "https://ccc.com:9001/ccc",
    "state": "pass",
    "category": "c2",
    "black": "yes",
    "pc": "pc-c",
    "uid": "user-c",
    "mail": "ccc@xxx.com",
    "mail-src": {
      "ccc@yyy.com": [
        "test cy1"
      ],
      "ccc@zzz.com": [
        "test cz1"
      ]
    }
  }
]

そして実際にChatGPTに作成してもらった Pythonプログラムは以下の通りです。(すこし自分の環境に合わせて修正はしていますが。)

import csv
import json

# CSVファイルを読み込み、データを取得
# 
# 最初の部分では、mail_src.csv、mail_data.csv、url_data.csv という
# 3つのファイルをそれぞれ辞書形式に読み込みます。
# これにより、各ファイルのデータが辞書のリストとして取得されます。
# それぞれの辞書内には、CSVのヘッダーがキーとして使用され、
# 各行のデータがそれぞれの辞書として格納されます。

with open('mail_src.csv', 'r') as file:
    mail_src_data = list(csv.DictReader(file))
    
with open('mail_data.csv', 'r') as file:
    uid_data = list(csv.DictReader(file))

with open('url_data.csv', 'r') as file:
    dest_data = list(csv.DictReader(file))

# メールアドレスと件名をマッピングする辞書を作成
# 
# mail_src.csv ファイルの内容から、メールアドレスと件名のマッピングを
# 作成します。mail_subject_map という辞書を作成し、
# メールアドレスをキーにして、それに関連する件名をリストとして格納します。

mail_subject_map = {}
for entry in mail_src_data:
    mail = entry['mail-src']
    subject = entry['subject'].replace('\u201d', '').replace('\"', '')
    if mail in mail_subject_map:
        mail_subject_map[mail].append(subject)
    else:
        mail_subject_map[mail] = [subject]

# JSONデータを構築
#
# dest_data の情報を元にして新しい JSON データを構築します。
# 各行の情報を元に、dest_entry という辞書を作成し、'dest'、'state'、
# 'category'、'black'、'pc'、'uid'、'mail'というキーを持たせます。

json_data = []
for entry in dest_data:
    uid = entry['uid']
    mail = entry['mail']
    dest_entry = {
        'dest': entry['dest'],
        'state': entry['state'],
        'category': entry['category'],
        'black': entry['black'],
        'pc': entry['pc'],
        'uid': uid,
        'mail': mail,
        'mail-src': {}
    }
    # mail-src内のデータを整形
    #
    # 'mail-src' キーに関連する情報を整形します。
    # これは、mail_data.csv と mail_src.csv の情報を組み合わせ、
    # 指定された形式で'mail-src' キーの中身を構築するための処理です。

    if uid in [row['uid'] for row in uid_data]:
        mail_src = [x.strip() for x in next(row['mail-src'] for row in uid_data if row['uid'] == uid).split(',')]
        for src in mail_src:
            if src in mail_subject_map:
                dest_entry['mail-src'][src] = mail_subject_map[src]
    json_data.append(dest_entry)

# JSON形式に変換して出力
#
# json.dumps() を使用して json_output に JSON 形式のデータを出力します。
# これにより、json_output には指定された形式の JSON データが格納されます。
# そして、print(json_output) により、この JSON データが出力されます。

json_output = json.dumps(json_data, indent=2)
print(json_output)

人間が作るとなるとPythonに詳しい人でも数時間はかかりそうですが、ChatGPT は一瞬で作ってしまうところがすごいですね。

また、私はPython初心者なので、見たことないような関数を使われていると勉強になります。

Python で requests を使って Webサイトにアクセスする際に、タイムアウトを設定してみた

前のブログで bandit を利用し、Webサイトのスクレイピングを行っている pythonプログラムのセキュリティチェックを行ってみました。

http://k2-ornata.com/python_security_check_with_bandit/

すると、上記ブログにも記載したとおり「Requests call without timeout」という警告が出てしまった為、警告の指示通り、requests の部分に timeout を設定してみることにしました。

1.修正前

修正前は、ただ「requests.get(url)」としているだけでした。

response = requests.get(url)
html = response.content.decode("utf-8")

2.修正後

「requests.get(url)」としていた部分を「requests.get(url, timeout=(6.0, 10.0))」とし、タイムアウトが発生した際の例外処理を行っています。

from requests.exceptions import Timeout
・・・
# Get the HTML content of the top page
try:
    response = requests.get(url, timeout=(6.0, 10.0))
    html = response.content.decode("utf-8")
except Timeout:
    print(f"\n[Timeout {url}]")

なお、timeout の後ろに2つ数字を書いている理由ですが、
timeoutには、connect timeout(前) と read timeout(後) の2種類がある為です。

この2種類のタイムアウトについては、以下のサイトで詳しく説明されていますのでご紹介しておきます。(ありがとうございました!)

【Python】requestsを使うときは必ずtimeoutを設定するべき(Cosnomi Blog)https://blog.cosnomi.com/posts/1259/

3.bandit 実行結果

以下が修正後の pythonプログラムに対する実行結果です。

「Test results:」が「No issues identified.」となっており、問題が解消されたことが確認できました。

% bandit test.py
[main]	INFO	profile include tests: None
[main]	INFO	profile exclude tests: None
[main]	INFO	cli include tests: None
[main]	INFO	cli exclude tests: None
[main]	INFO	running on Python 3.8.3
[node_visitor]	WARNING	Unable to find qualified name for module: test.py
Run started:2023-08-14 08:04:48.814175

Test results:
	No issues identified.

Code scanned:
	Total lines of code: 53
	Total lines skipped (#nosec): 0

Run metrics:
	Total issues (by severity):
		Undefined: 0
		Low: 0
		Medium: 0
		High: 0
	Total issues (by confidence):
		Undefined: 0
		Low: 0
		Medium: 0
		High: 0
Files skipped (0):
%

bandit を使って pythonコードのセキュリティチェックを行ってみた

python言語ってC言語などにくらべると安全そうなイメージがありますが、やはりセキュリティには気をつけないといけない、ということで 開発した python コードが安全かどうか調べるツールがないか調べてみました。

すると、bandit というツールが無償で利用できることがわかりました。

そこでこのツールを使って自分で開発した python プログラムのセキュリティチェックを行ってみましたのでここに記録しておきます。

なお、この bandit でチェックする前に、python のコーディング規約チェッカーである PEP 8 を使ってある程度(- -; コーディング規約に従うようにプログラムを修正しております。

この PEP 8 の PyCharm での利用方法については以下のサイトに記載しております。

http://k2-ornata.com/pychart_pep-8_check/

それでは bandit をインストールするところから行きたいと思います。

1.bandit のインストール

bandit のインストールは非常に簡単です。

pip がインストールされていれば、以下のコマンドを実行するだけです。

$ pip install pandit

なお、私の環境では pythonのバージョンがデフォルトで 2.X系になってしまっていた為、以下のような警告が出てしまいました。

bandit インストール時の警告

これを3系に変えようと思い pyenv を利用してみましたが一筋縄ではいかず、以下のサイトに助けていただきうまくいきました。ありがとうございました。m(_ _)m

pyenvでPythonのバージョンが切り替わらないときの対処方法【Mac Apple silicon環境】(ヒトリセカイ)https://hitori-sekai.com/python/error-pyenv/

2.banditの実行

bandit の実行も非常に簡単です。以下のとおりコマンドを打つだけです。

$ bandit <プログラム名>

実際に自分で作成したスクレイピングを行う pythonプログラムに対して実行してみた結果は以下の通りです。

bandit 実行結果

ちょっと上の画像は見にくいので、テキストでも掲載しておきます。

% bandit test.py
[main]	INFO	profile include tests: None
[main]	INFO	profile exclude tests: None
[main]	INFO	cli include tests: None
[main]	INFO	cli exclude tests: None
[main]	INFO	running on Python 3.8.3
[node_visitor]	WARNING	Unable to find qualified name for module: test.py
Run started:2023-08-14 01:37:45.050500

Test results:
>> Issue: [B113:request_without_timeout] Requests call without timeout
   Severity: Medium   Confidence: Low
   CWE: CWE-400 (https://cwe.mitre.org/data/definitions/400.html)
   More Info: https://bandit.readthedocs.io/en/1.7.5/plugins/b113_request_without_timeout.html
   Location: test.py:31:11
30	# Get the HTML content of the top page
31	response = requests.get(url)
32	html = response.content.decode("utf-8")

--------------------------------------------------
>> Issue: [B113:request_without_timeout] Requests call without timeout
   Severity: Medium   Confidence: Low
   CWE: CWE-400 (https://cwe.mitre.org/data/definitions/400.html)
   More Info: https://bandit.readthedocs.io/en/1.7.5/plugins/b113_request_without_timeout.html
   Location: test.py:62:15
61
62	    response = requests.get(link)
63	    html = response.content.decode("utf-8")

--------------------------------------------------

Code scanned:
	Total lines of code: 46
	Total lines skipped (#nosec): 0

Run metrics:
	Total issues (by severity):
		Undefined: 0
		Low: 0
		Medium: 2
		High: 0
	Total issues (by confidence):
		Undefined: 0
		Low: 2
		Medium: 0
		High: 0
Files skipped (0):
%

3.bandit 実行結果の考察

どうやら「Test results:」という部分にプログラムの問題点が列挙されているようです。

2つ「Issue」が記載されていますが、どちらも Requests call without timeout となっています。

実際このプログラムでは指定したWebサイトからデータを取ってきているのですが、その時にタイムアウトの処理をしていませんでした。

たしかにこのままだと、Webサイトから応答が返ってくるまで繋ぎっぱなしになってしまい、そのまま何度もこのプログラムを実行するとサーバもクライアントもリソーズ不足になってしまいそうです。

改めて、こういったセキュリティチェック(ツール)の重要性を認識することができました。

PyChart のPEP 8(コーディング規約)チェックを強化してみた(Mac版)

最近たまにPythonのプログラムを作成することがあるのですが、そんな中、PEP 8 というPythonのコーディング規約があることを知りました。

そこで PEP 8 を試しに使ってみようと思い調べていたところ、pythonなどの開発環境であるPyChart には、すでにそのチェック機能が実装されているとのことでした。

しかしながらそのチェック(警告)の設定がデフォルトでは「Weak Warning」となっており、あまり目立たなくなっていた為、試しに「Error」となるように設定変更してみました。

1.PyCharmの「Preference」から設定変更

PyCharmの「Preference」を選択すると、以下のポップアップ画面が表示されますので、その中から下図の通り、

「Editor」-「Inspections」-「PEP 8 coding style violation」

と選択していきます。

PyCharm – Preference

すると Severity を設定するところがありますので、「Weak Warning」から「Error」 に変えてみました。(「Warning」でもよかったですが。。)

2.自作のpythonをチェックしてみる

1.の設定変更後、PyChart の編集画面にて自作の pythonを表示させてみたところ、下図の赤枠の通り、右上にエラーが11件あることが表示されていました。

PyChart – PEP 8 エラー警告

さらにその赤枠の部分をクリックすると、画面下に新しいウィンドウが開き、エラーの一覧が表示されました。

大半が “,(カンマ)” や “#(コメント)”の後ろに「スペース」が無いというものでしたが、複数の人間で共同開発する場合は、こういった規約に従うことが重要なのだと思います。

スクレイピング:指定した文字と文字の間のhtmlデータを全て取り出す

タイトルの通り、何かのhtml タグではなく、ブラウザに表示される文字列のスタート部分とエンド部分を指定して html データを抜き出してみました。

1.Pythonプログラム

# -*- coding: utf-8 -*-
from datetime import datetime, timedelta
import requests
import re
from bs4 import BeautifulSoup

# Set the day before yesterday's date
beforeyesterday = datetime.now() - timedelta(days=17)
beforeyesterday_str = beforeyesterday.strftime("%Y/%m/%d")

# URL of the top page
url = "https://www.xxx.jp/"・・・スクレイピングサイト"/"付き
domain ="https://www.xxx.jp"・・・スクレイピングサイト"/"無し

# start_string と end_string の間のストリングを取得する
start_string = "○○の一例:"
end_string = "○○○した場合は、○○○までご連絡ください。"

# Get the HTML content of the top page
response = requests.get(url)
html = response.content.decode("utf-8")

# Use BeautifulSoup to parse the HTML content
soup = BeautifulSoup(html, "html.parser")

# Find the parent element with the "○○○情報" title
security_info = soup.find('h1', string="○○○情報").parent
#print(security_info)

# Find all <h3> tags within the parent element
h3_tags = security_info.find_all('h3')

filtered_links = [tag.a['href'] for tag in h3_tags if tag.a is not None and beforeyesterday_str in tag.a.text]

for link in filtered_links:
    if link.startswith('http'):
        print(link)
    else:
        link = domain + link
        print(link)

    # トップページから取得した指定日の記事を読み込む
    response = requests.get(link)
    html = response.content.decode("utf-8")

    # BeautifulSoupを使ってHTMLを解析
    # soup = BeautifulSoup(html, 'html.parser')
    # article = soup.find('article', class_="nc-content-list")
    # print(article)

    # texts_p = [c.get_text() for c in article.find_all('p')]
    # print(texts_p)

    # Split the target text on the start and end strings and take the middle part
    target_text = html.split(start_string)[1].split(end_string)[0].split("</p>")[0]

    print(target_text)

    # 改行コードを空文字列に置換して一つのテキストにする
    target_text = target_text.replace('\n', '')

    # <br />タグを区切り文字として順番に配列に入れる
    result_array = [text for text in target_text.split('<br />') if text]

    # 結果の出力
    print(result_array)

2.プログラムのポイント

ChatGPTと相談しながらつくったのではっきりわかってないところはありますが^^、大体以下の意味合いかと思います。

タグとストリングで検索し親タグを含めたデータを取得

# この方法<p>タグなどには使えないかもしれません。

27行目:security_info = soup.find(‘h1’, string=”○○○”).parent
これは”○○○”というストリングが含まれる<h1>タグを見つけて、その1つ上位層のタグまでのデータをsecurity_infoに渡してくれているようです。

31行目:h3_tags = security_info.find_all(‘h3’)
21行目のsecurity_infoから<h3>タグの部分だけ抜き出してh3_tagsに入れてくれているようです。

33行目:filtered_links = [tag.a[‘href’] for tag in h3_tags if tag.a is not None and beforeyesterday_str in tag.a.text]
ここやたら長いですが、結果的には
h3_tagsからbeforeyesterday_strが含まれるhrefのリンクデータのみ抜き出し、filtered_linksに入れてくれているようです。

スタートとエンドの文字列を指定して抜き出し、お尻の不要なデータを削除

57行目:target_text = html.split(start_string)[1].split(end_string)[0].strip().split(“</p>”)[0]
ここも長いですが、
htmlに読み込まれているWebページのデータについて、start_string から end_stringまでのデータを抜き出してくれているようです。
また、split(“</p>”)[0]をつけることで、お尻にくっついていた</p>タグ以降を削除しています。

3.実行例

以下、実際にプログラムを実行した際の出力結果です。

https://web.xxx.jp/faqs/xxxx
○○○のサイズがクォータ制限に達しました<br />○○○のストレージ容量が少ない<br />~ 受信メールエラー<br />○○○のストレージがいっぱいです、○○○が一時停止されています
['○○○のサイズがクォータ制限に達しました', '○○○のストレージ容量が少ない', '~ 受信メールエラー', '○○○のストレージがいっぱいです、○○○が一時停止されています']

スクレイピング:Webページの指定したH5タグの配下にあるデータを抽出する

WebページのHTMLの指定した<h5>タグの配下の、<div>タグの中のデータを取得するプログラムを作成しましたので、記録として残しておきます。

<h5 class="alert_h5">○○○の件名</h5>
<div class="dit_a">
<p>【重要】(○○○)<br>
【重要・緊急】○○○のお知らせ<br>
【○○○】○○○等の確認について<br>
<br>
※○○○<br></font></p>
</div>

1.作成したPythonプログラム

以下作成したサンプルプログラムです。

# -*- coding: utf-8 -*-
from datetime import datetime, timedelta
import requests
import re
from bs4 import BeautifulSoup

# Set the day before yesterday's date
beforeyesterday = datetime.now() - timedelta(days=15)
beforeyesterday_str = beforeyesterday.strftime("%Y%m%d")
mail_line_pattern = "From: \"[a-zA-Z0-9_.+-]+.+[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+\""
mail_pattern = "^[0-9a-zA-Z_.+-]+@[0-9a-zA-Z-]+\.[0-9a-zA-Z-.]+$"
env_mail_pattern = "<+[0-9a-zA-Z_.+-]+@[0-9a-zA-Z-]+\.[0-9a-zA-Z-.]+>"
subject_line_pattern = "Subject:"

# Initialize an empty list to store the articles
articles_beforeyesterday = []
text_beforeyesterday = []
link_beforeyesterday = []
mail_list = []
email_list = []
env_email_list = []
subject_list = []
title_list = []

# URL of the top page
url = "https://www.xxx.jp/news/"
domain ="http://www.xxx.jp"

# Get the HTML content of the top page
response = requests.get(url)
html = response.content.decode("utf-8")

# Use BeautifulSoup to parse the HTML content
soup = BeautifulSoup(html, "html.parser")

# Find all <a href> elements in the HTML
for a in soup.find_all("a",href=re.compile(beforeyesterday_str)):
    if a in articles_beforeyesterday:
        print("duplicated")
    else:
        text=a.getText()
        link=a.get("href")
        articles_beforeyesterday.append(a)
        text_beforeyesterday.append(text)
        link_beforeyesterday.append(link)

print(link_beforeyesterday)

for link in link_beforeyesterday:
    # Get the HTML content of the top page

    if link.startswith('http'):
        print(link)
    else:
        link = domain + link
        print(link)

    response = requests.get(link)
    html = response.content.decode("utf-8")

    # Use BeautifulSoup to parse the HTML content
    soup = BeautifulSoup(html, "html.parser")

    h5_tags = soup.find_all('h5', class_='alert_h5')

    for tag in h5_tags:
        if tag.get_text() == '○○○の件名':
            print(tag.find_next('p').get_text())

2.プログラムの解説

今回のポイントは2つあります。

データが指定した文字で始まっているか確認

1つ目は53 から57行目になります。

「if link.startswith(‘http’):」でlinkに入っているデータが’http’で始まっている場合、printしているだけですが、始まっていない場合、”http://www.xxx.jp”というデータを頭にくっつけています。
 これは今回対象となったサイトのリンクがいきなりディレクトリで始まっていた為です。

指定したデータが含まれるタグ配下のデータを取得

2つ目のポイントは最後の4行になります。

 「○○○の件名」が含まれる “h5″タグを見つけて、その配下の “p”タグで囲まれた文字列をピックアップしています。

3.実行結果

このプログラムの実行結果(出力)例は以下の通りです。

duplicated
['/news/xxxxxx_20230728.html', 'http://www.xxx.jp/news/xxxxx_20230728.html']
http://www.xxx.jp/news/xxxxxx_20230728.html
【重要】(○○○)
【重要・緊急】○○○のお知らせ
【○○○】○○○等の確認について

※○○○
http://www.xxx.jp/news/xxxxxx_20230728.html
【重要】(○○○)
【重要・緊急】○○○のお知らせ
【○○○】○○○等の確認について

※○○○

スクレイピング:Webページからhrefの中に指定した文字が含まれる記事を抜き出し、その記事から必要なデータを抽出する

指定したサイトから一昨日の日付を<a href>のリンクに持つ記事をピックアップし、その記事の中からメールアドレスとメール件名を抽出する pythonをChatGPT code interpreterの手も借りながら作成しましたので記録として残しておきます。

1.作成したpythonコード

最終的に作成したpythonコードは以下のとおりです。

# -*- coding: utf-8 -*-
from datetime import datetime, timedelta
import requests
import re
from bs4 import BeautifulSoup

# Set the day before yesterday's date
beforeyesterday = datetime.now() - timedelta(days=2)
beforeyesterday_str = beforeyesterday.strftime("%Y%m%d")
# mail_line_pattern = "From: \"[a-zA-Z0-9_.+-]+.+[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+\""
mail_line_pattern = "From:"
mail_pattern = "^[0-9a-zA-Z_.+-]+@[0-9a-zA-Z-]+\.[0-9a-zA-Z-.]+$"
env_mail_pattern = "<+[0-9a-zA-Z_.+-]+@[0-9a-zA-Z-]+\.[0-9a-zA-Z-.]+>"
subject_line_pattern = "Subject:"

# Initialize an empty list to store the articles
articles_beforeyesterday = []
text_beforeyesterday = []
link_beforeyesterday = []
mail_list = []
email_list = []
env_email_list = []
subject_list = []
title_list = []

# URL of the top page
url = "https://www.xxx.jp/news/"・・・スクレイピングするサイトのURL

# Get the HTML content of the top page
response = requests.get(url)
html = response.content.decode("utf-8")

# Use BeautifulSoup to parse the HTML content
soup = BeautifulSoup(html, "html.parser")

# Find all <a href> elements in the HTML
for a in soup.find_all("a",href=re.compile(beforeyesterday_str)):
    if a in articles_beforeyesterday:
        print("duplicated")
    else:
        text=a.getText()
        link=a.get("href")
        articles_beforeyesterday.append(a)
        text_beforeyesterday.append(text)
        link_beforeyesterday.append(link)

print(articles_beforeyesterday)

for link in link_beforeyesterday:
    # Get the HTML content of the top page
    response = requests.get(link)
    html = response.content.decode("utf-8")

    # Use BeautifulSoup to parse the HTML content
    soup = BeautifulSoup(html, "html.parser")
    print(link)
    mail_list=soup.find_all(string=re.compile(mail_line_pattern))
    
    for mail in mail_list:
        # email = re.findall(mail_line_pattern, mail.replace('\n', ''))
        # ヘッダーメールアドレスが日本語の場合もあるので、以下に修正
        # lstrip()・・・左端の文字(\n)を削除
    # strip(mail_line_pattern)・・・"From:"を削除
        # split('<')[0]・・・"<"以降を削除
        # replace('"', '')・・・ダブルクォーテーションを削除
        email = mail.lstrip().strip(mail_line_pattern).split('<')[0].replace('"', '')
        env_email = re.findall(env_mail_pattern, mail.replace('\n', ''))
        email_list.append(email)
        env_email_list.append(env_email)
    print(email_list)
    print(env_email_list)

    subject_list=soup.find_all(string=re.compile(subject_line_pattern))
    for title in subject_list:
        email_title_line = title.replace('\n', '')
        email_title = email_title_line.replace('Subject: ', '')
        title_list.append(email_title)
    print(title_list)

    # 配列の初期化
    mail_list = []
    email_list = []
    env_email_list = []
    subject_list = []
    title_list = []

2.pythonコード解説

指定した文字列が含まれるhref のリンクをピックアップ

38行目:for a in soup.find_all(“a”,href=re.compile(beforeyesterday_str)):
ここで、reライブラリを利用し一昨日の日付が href のリンクの中に含まれているものを抜き出しています。

指定した文字列で始まる行をピックアップ

59行目:mail_list=soup.find_all(string=re.compile(mail_line_pattern))
では、タグを指定せず、soup に取り込まれた HTMLページからmail_line_patternの正規表現に合致するものを抜き出しています。

文字列からいろいろ削除する

67行目:email = mail.lstrip().strip(mail_line_pattern).split(‘<‘)[0].replace(‘”‘, ”)
コメントも書いていますが、元々の文字列からいろいろな方法でデータを削除し、必要なものだけにしています。

3.実行結果

なお、この時の実行結果は以下の通りです。

% python3 scraping.py
python3 info-tech-center.py
duplicated
[<a href="https://www.xxx.jp/news/20230621xxxxxx.html">○○メールに関する注意喚起a</a>]
https://www.xxx.jp/news/20230621xxxxxx.html
[['From: "xx.xxx.ac.jp"'], ['From: "xx.xxx.ac.jp"'], ['From: "Postmaster@xx.xxx.ac.jp"']]
[['<xxxx-xxxxxxxx@xxx.xxxxxxx.ne.jp>'], ['<xxx@xxxxxxxxxxx.co.jp>'], ['<xxxx@xx.uec.ac.jp>']]
['xxxx@xx.uec.ac.jp 電子メール通知', 'メール認証 xxxx@xx.uec.ac.jp', 'メールボックスのストレージがいっぱいです。アカウントは停止されています。確認が要求されました']

<参考サイト>

・PythonとBeautiful Soupでスクレイピング(Qiita)(https://qiita.com/itkr/items/513318a9b5b92bd56185)
・Python配列のループ処理(Qiita)(https://qiita.com/motoki1990/items/d06fc7559546a8471392)
・図解!Python BeautifulSoupの使い方を徹底解説!(select、find、find_all、インストール、スクレイピングなど)(https://ai-inter1.com/beautifulsoup_1/#st-toc-h-19)
・Pythonで文字列を抽出(位置・文字数、正規表現)(note.nkmk.me)(https://note.nkmk.me/python-str-extract/#_12)
・サルにも分かる正規表現入門(https://userweb.mnet.ne.jp/nakama/)
・Pythonで特定の文字以降を削除する(インストラクターのネタ帳)(https://www.relief.jp/docs/python-get-string-before-specific-character.html)
・【Python】文字列を「〜で始まる/終わる/〜が含まれる」で抽出する方法(てっくらぶ)(https://www.tecrab.com/articles/python-starts-ends-with-in.html)
・[Python コピペ] 文字列内のダブルクォーテーション(”)、シングルクォーテーション(’)の削除方法(資産運用を考えるブログ)(https://kabu-publisher.net/index.php/2021/10/31/16/python_double_single1/)

AbuseIPDBにアクセスするPythonプログラムを試してみた

ある理由から AbuseIPDB にIPアドレスを投げてその評価結果を受け取るPythonプログラムを作成しようと思いググったところ、以下のYoutube動画を見つけました。

AbuseIPDB API Python Script(Mostafa Yahia)

そこで真似して作成してみることにしました。

なお、上記動画で紹介しているPythonプログラムは以下のGitHubで公開されています。

https://github.com/Mostafayahia-hunter/AbuseIPDB-API-Python

だだし、最近、AbuseIPDBからのレスポンスが変わったのか、一部の箇所でエラーがでたので、csv_columnsの定義の部分を以下の通り書き換えました。(’isTor’を追加しました。)

csv_columns = ['ipAddress','isPublic','ipVersion','isWhitelisted','abuseConfidenceScore','countryCode','usageType','isp','domain','hostnames','isTor','totalReports','numDistinctUsers','lastReportedAt']

また、Pythonプログラム実行時に要求される IPリストとして入力するcsvファイルとしては、以下のような内容を用意しました。

IP,
88.29.56.3,
1.158.10.11,
161.35.146.242,
177.84.141.183

そしてコマンドプロンプト上で実行した結果がこちらです。

Python実行画面

実行するとAbuseIP_results.csvというファイルが出来上がっており、それを開いてみた結果は以下の通りです。

Python実行結果

プログラムをいろいろいじってみて CSVライブラリの使い方あたりがとても勉強になりました。

PythonにてVirusTotalのAPIにアクセスし、URLの危険度情報を取得してみた

前回まではPythonを使ってWebサイトの情報をスクレイピングしました。

その際にPython用に提供されているさまざまなライブラリを利用すれば比較的簡単に情報が抜き取れることを確認しました。

http://k2-ornata.com/python_scraping_table/

一方で、最近では多くのWebサイトの方で情報にアクセスする為のAPIを提供しており、それを利用すればもっと簡単、確実に必要な情報を取り出すことが可能となっています。

そこで今回は、そんなWebサイトの例としてVirusTotalが公開しているAPIにPythonからアクセスし、情報を取得してみたいと思います。

1.VirusTotalにアクセスする為のAPI Key取得

PythonからVirusTotalのAPIにアクセスする為には、まず、API Keyという自分の身元を証明するKeyを取得する必要があります。

そのAPI Key をPythonのプログラムを経由してVirusTotalに渡すことで、初めてWebサイトのAPIを利用することが可能になります。

API Key はVirusTotalのアカウント情報に紐づいていますので、もしまだアカウントを持っていなければ、それを作るところから始めます。

VirusTotalへのサインアップ

以下のVirusTotalのトップページ右上にある「Sign up」からアカウントの登録をしましょう。

VirusTotalのトップページ

アカウント情報を登録すると自分のメールアドレスにSign upを完了する為のメールが送られてきます。

そのメールのリンクをクリックすることでアカウントの登録が完了します。

VirusTotalへのログインとAPI Keyの取得

登録が完了したらさっそくVirusTotalにログインします。

ログインするとページの右上に自分のユーザアイコンが表示されています。

ユーザアイコンから「API Key」を選択

そのユーザアイコンをマウスでクリックすると、プルダウンリストに「API key」が表示されています。

それをクリックするとAPI Keyや利用制限などの情報が記載されたページが表示され、自分の API Key が確認できます。

2.PCに「vt-py」ライブラリをインストール

VirusTotal からそのサイトのAPIに簡単にアクセスする為のPython用のライブラリ「vt-py」が提供されています。

それをPyCharmなどの開発環境にインストールします。

PyCharmに「vt-py」を取り込み

あたらしいライブラリをPyCharmに導入する詳しい操作方法については、私の以前のブログにも記載していますので参考にしてみてください。

3.Pythonでプログラム開発

そしていつもどおり、おもむろにプログラムを開発します。

import vt

apikey = "<API Key>"
url = "<URL>"

# access to VirusTotal and get URL information(object)
client = vt.Client(apikey)
url_id = vt.url_id(url)
url = client.get_object("/urls/{}", url_id)

client.close()

# print each attribute from object
print(url.get("categories"))
print(url.get("last_analysis_date"))
print(url.get("times_submitted"))
print(url.get("last_analysis_stats"))

2行目の<API Key>の部分は自分のAPI Key を “8・・・・・a”のような感じで埋め込んでください。

また3行目の<URL>については、例えば yahooについて調べる場合は、”https://yahoo.co.jp”のような感じで埋め込んでください。

なお今回は、Pythonのプログラムに直接API Keyを埋め込む形になっていますが、正しくはAPI Key情報は別のファイルに保存し、しっかり暗号化などしておいてほうがよさそうです。

うっかり漏らしてしまうと、自分になりすましてアクセスされてしまいますよー。

4.Pythonの実行

実際にあるサイトについて実行した結果は以下の通り harmless : 87, malicious : 0 となっていました。問題ないという判断のようですね。

{'Forcepoint ThreatSeeker': 'search engines and portals', 'Sophos': 'portal sites', 'alphaMountain.ai': 'Search Engines/Portals', 'Comodo Valkyrie Verdict': 'mobile communications', 'BitDefender': 'news'}

1654330912

489

{'harmless': 87, 'malicious': 0, 'suspicious': 0, 'undetected': 8, 'timeout': 0}

ちなみに自分のサイト(k2-ornata.com/)を調べようとしたところ、以下のとおり知らんと言われましたー。

vt.error.APIError: ('NotFoundError', ・・・

それなりに良い意味でも悪い意味でも有名なサイトでないといけないようです。^^;
まあ、すでにDBに格納されているデータから情報を取り出しているのであ当たり前ですね。。。

5.他にどんな情報が取れるのかは要確認・・・

今回は指定したURLに関して以下の4つの情報が取れることがわかりました。

categories
last_analysis_date
times_submitted
last_analysis_stats

しかし、その他どんな情報がとれるのかについては調べきることができませんでしたので、今後の課題にしておきたいと思います。

<参考サイト>

・VirusTotalの公式APIライブラリ「vt-py」を使ってみた。(たれながし.info)
https://tarenagashi.hatenablog.jp/entry/2021/06/09/223105
・vt-py Quickstart(VirusTotal)
https://virustotal.github.io/vt-py/quickstart.html#get-information-about-an-url

PythonにてPandasを使ってWebページのテーブルのセルの内容を抜き取ってみた

前回はRequests と Beautiful Soupを使ってWebページの特定のタグの情報を読み込んで見ました。

http://k2-ornata.com/python_web_scraping/

しかし、そのライブラリだけだとWebページ内のテーブルの情報を抜き出すのは大変そうだなあとおもっていました。

そんな中、Pandasというライブラリを使えば簡単にテーブルの情報を抜き出せるようでしたので、トライしてみました。

1.ライブラリの読み込み

まずは必要なライブラリをPyCharmに取り込みます。
今回、Pandasとlxmlを初めてPyCharmで利用するので、以下のように取り込んでいます。

ライブラリの取り込み

2.プログラミング

そしていつものように^^、おもむろにプログラミングします。

なお、今回スクレイピングしているサイトも、前回同様、私のブログ
http://k2-ornata.com/security-countermeasure/
となっています。

import pandas as pd

url = 'http://k2-ornata.com/security-countermeasure/'
dfs = pd.read_html(url)

# table count
print(len(dfs))

# head 5 line print
print(dfs[0].head())

# cell[0,0] print
print(dfs[0].iat[0,0])

# cell[0,1] print
print(dfs[0].iat[0,1])

# first column only print
print(dfs[0].iloc[:,[0]])

簡単に内容を説明すると、

最初の3行・・・ライブラリを読み込んだあと、Webページを読み込み、dfsに格納
次の2行・・・Webページ内のテーブルの数を表示
次の2行・・・最初のテーブルdfs[0]の先頭5行を表示
次の2行・・・最初のテーブルdfs[0]のセル[0,0]の内容を表示
次の2行・・・最初のテーブルdfs[0]のセル[0,1]の内容を表示
最後の2行・・・最初のテーブルdfs[0]の1列目[0]を表示

となっています。

そして以下が実行結果です。(見やすくする為に各printの間に1行入れています。)

13

      0                                                  1
0    概要  CSPM(Cloud Security Posture Management)クラウドの設定...
1  参考文献  改めて知るCWPP(Cloud Workload Protection Platform)と...

概要

CSPM(Cloud Security Posture Management)クラウドの設定に不備がないかチェックし、不備があれば自動的に修正までしてくれる製品と思われる。CWPP(Cloud Workload Posture Management)オンプレやクラウド上の様々なワークロード(※)における脆弱性管理やセキュリティ保護を行う製品と思われる。両者をどちらもカバーする製品として、パロアルト社のPrisma Cloudがあり、CNSP(Cloud Native Security Platform)と読んでいる。※ワークロードっていう言葉はいつきいてもあまりピント来ませんが、物理マシンや仮想マシン、コンテナ、サーバレスなどを指すらしい。

      0
0    概要
1  参考文献

3.Pamdasの使い方

Pandasを使うと今回のように、Webページ内のテーブルの数や、特定のテーブルの情報、さらにその中テーブルの中の特定のセルなどの情報が読み取れるようです。

とりあえず今回使用したものを簡単に説明しておきます。

len(dfs)

dfs の中にWebページ内の全てのテーブル情報が書き込まれ、len()関数を使うことで、そのテーブルの数(Webページに全部で何個テーブルがあるか)をカウントできます。

dfs[テーブル番号]

dfs[テーブル番号]という形で指定することで、ページの先頭から何番目のテーブルの情報かを指定することができます。

dfs[テーブル番号].head()

.head()関数を使うことで、指定したテーブルの先頭から5行目までを取得することができます。

dfs[テーブル番号].iat[行,列]

.iat[行,列]を使うことで、指定したテーブルのどのセルの情報を取得するかを指定することができます。

dfs[テーブル番号].iloc[:,[列]]

.iloc[:,[列]]を使うことで、指定したテーブルの特定の列の情報だけを取得することができます。
なお、”:”の記述はとりあえずおまじないとおもっておけばいいと思います。(あまりよくわかってないのでー。)

4.最後に

過去3回に渡ってPythonを触ってみましたが、Pythonにはいろんなライブラリがあり、それをうまく使えばいろんなことがとても簡単にできるなあと実感しました。

Pythonが人気があるのもわかる気がしますね。

<参考サイト>

・Python, pandasでwebページの表(htmlのtable)をスクレイピング(note.nkmk.me)
https://note.nkmk.me/python-pandas-web-html-table-scraping/
・pandasで任意の位置の値を取得・変更するat, iat, loc, iloc(note.nkmk.me)
https://note.nkmk.me/python-pandas-at-iat-loc-iloc/