マヨケーがポアされたため、現在はロシケーがメインとなっています。

「利用者:夜泣き/スクリプト」の版間の差分

提供:唐澤貴洋Wiki
ナビゲーションに移動 検索に移動
>夜泣き
編集の要約なし
>夜泣き
編集の要約なし
1行目: 1行目:
修正中なのでコメントアウト。以下のなんたらかんたらになんたらかんたらがあれば[[利用者・トーク:夜泣き]]にどうぞ
‎とりあえず取り急ぎ。バグ報告は[[利用者・トーク:夜泣き]]
*curlになってるのをpython側でrequestsやBeautifulSoup入れて簡潔に処理する
== コード ==
**長所 保守性アップ、当職が書きやすい
<syntaxhighlight lang="python3" line>
**短所 Tailsだと電源オフのたびにpip install requestsしないと動かない
#!/usr/bin/env python3
 
<!--
#!/usr/bin/env python


'''
'''
11行目: 8行目:
前開発者との出会いに感謝
前開発者との出会いに感謝


ーーーーーーーー【注意点】ーーーーーーーーーー
〜〜〜〜〜〜〜〜〜〜〜〜〜【使い方】〜〜〜〜〜〜〜〜〜〜〜〜〜
・玉葱を使用すること。
・バグ報告はhttps://krsw-wiki.org/wiki/?curid=15799。


'''
・terminalに $ python3 (ファイル名) で作動します
・定数類は状況に応じて変えてください
・$ python3 (ファイル名) krsw コマンドライン引数をkrswとつけると自動モードです
・自動モードではユーザーは降臨ショー、クエリはなし、取得上限まで自動です
・つまりユーザー入力が要りません


#インポート類
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
import subprocess
import codecs
import re
from time import sleep


#定数類
ーーーーーーーーーーーーー【注意点】ーーーーーーーーーーーーー
##nitterのインスタンス
##生きているのはhttps://github.com/zedeus/nitter/wiki/Instancesで確認
Nitterinstance = 'https://nitter.skrep.in/'


##archive.todayの魚拓
・環境は玉葱前提です。
##実際にアクセスして魚拓があるか調べるのにはonionを使用
・Whonix-Workstationで動作確認済
Archivetoday = 'http://archiveiya74codqgiixo33q62qlrqtkgmcitqx5u2oeqnmn5bpcbiyd.onion/'
・bs4はインストールしないと標準で入ってません
 
・requestsも環境によっては入っていないかもしれない
##archive.todayの魚拓
$ pip install bs4 requests
##記事の文章に使うのはクリアネット
・pipも入っていなければ$ sudo apt install pip
Archivetodaystandard = 'https://archive.ph/'
・バグ報告はhttps://krsw-wiki.org/wiki/?curid=15799
 
 
Curlhead = 'torsocks curl -sS -A "Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0" '
Redirectcurl = ' -w "%{redirect_url}" -o /dev/null 2> /dev/null'
Searchquery = "search?q=from%3A"
Limit = 400
Andamp = '&' + 'amp;'
HTMLtext = '<div class="tweet-content media-body" dir="auto">'
 
def get_redirect_url(src_url):
    srcb_url = src_url
    srca_url = subprocess.run(Curlhead + srcb_url + Redirectcurl, stdout=subprocess.PIPE,shell=True).stdout.decode('utf-8')
    while srca_url != '':
        srcb_url = srca_url
        srca_url = subprocess.run(Curlhead + srcb_url + Redirectcurl, stdout=subprocess.PIPE,shell=True).stdout.decode('utf-8')
    else:
        return srcb_url
 
def make_txt(txt_data):
    txt_data = '{|class="wikitable" style="text-align: left;"\n' + txt_data
    with codecs.open('tweet.txt', 'w', 'utf-8') as f:
        f.write(txt_data)
    print("テキストファイル手に入ったやで〜")
    exit()
 
def get_newpage(source_url):
    for ratelimit_counter in range(8):
        new_page = subprocess.run(Curlhead + '"' + Nitterinstance + source_url + '"', stdout=subprocess.PIPE,shell=True).stdout.decode('utf-8')
        if HTMLtext in new_page:
            return new_page
            break
        else:
            sleep(3)
    else:
        print('インスタンス 通信制限 多すぎ 無能 スクリプト 続行不能 強制終了')
        make_txt(output_txt)
 
account_flag = True
while account_flag:
    print("アカウント名を入れなければない。")
    account_str = input()
    account_check = subprocess.run(Curlhead + '"' + Nitterinstance + account_str + '"' + ' | grep "<title>"', stdout=subprocess.PIPE,shell=True).stdout.decode('utf-8')
    if 'Error' in account_check:
        print(account_str + "は実在の人物ではありませんでした")
    elif account_check == "":
        print("インスタンスが死んでいるまたは末尾の/が抜けてますを")
        exit()
    else:
        print("最終的に出会ったのが@" + account_str + "だった。")
        account_flag = False
query_flag = True
while query_flag:
    print("検索クエリを入れるナリ。空欄を入れると検索開始ナリ。")
    query_str = [account_str]
    query_input = input()
    while query_input != '':
        query_str.append(query_input)
        query_input = input()
    query_check = get_newpage(Searchquery + '+'.join(query_str))
    if 'No items found' in query_check:
        print("適切なクエリを入力することを切に望む。")
    else:
        print("クエリのピースが埋まっていく。")
        query_flag = False
print("ここにツイートの本文を入れる!すると・・・・なんと一致するツイートで記録を中断する!(これは本当の仕様です。)ちなみに、空欄だと" + str(Limit) + "件まで終了しない。")
end_blank = True
end_str = input()
if end_str != '':
    end_blank = False
output_txt = '|}'
tweet_list = query_check
 
for i in range(Limit):
    if not HTMLtext in tweet_list:
        newlist_url = "search" + subprocess.run('grep -m 1 \'<div class="show-more">\' | sed \'s/.*<a href="//\' | sed \'s/">Load more.*//\'',input=tweet_list.encode('utf-8'),stdout=subprocess.PIPE,shell=True).stdout.decode('utf-8').replace(Andamp,'&').replace('\n','')
        tweet_list = get_newpage(newlist_url)
    row_number = int(subprocess.run('grep -n -m 1 \'<div class="tweet-content media-body" dir="auto">\' | cut -f 1 --delim=\':\' ',input=tweet_list.encode('utf-8'),stdout=subprocess.PIPE,shell=True).stdout.decode('utf-8').replace('\n',''))
    tweet_url = 'https://twitter.com' + subprocess.run('sed -n ' + str(row_number - 3) + 'p | sed \'s/^.*a href="//\' | sed \'s/#m.*$//\'',input=tweet_list.encode('utf-8'),stdout=subprocess.PIPE,shell=True).stdout.decode('utf-8').replace('\n','')
    if tweet_url == 'https://twitter.com':
        print("急に残りツイートが無くなったな終了するか")
        break
    elif account_str.lower() not in tweet_url.lower():
        tweet_list = tweet_list.replace(HTMLtext, '済', 1)
        print('リツイートなのでポア')
        continue
    archive_url = subprocess.run(Curlhead + '"' + Archivetoday + tweet_url + '" | grep -m 1 "display:block;color:blue;font-size:16px;word-break:break-word" | sed \'s/.*display:block;color:blue;font-size:16px;word-break:break-word" href="//\' | cut -f 1 --delim=\'"\' | sed \'s#https\\{0,1\\}://[^/]*/#' + Archivetodaystandard + '#\'',stdout=subprocess.PIPE,shell=True).stdout.decode('utf-8').replace('\n','')
    if Archivetodaystandard not in archive_url:
        print(tweet_url + "の魚拓がない。これはいけない。")
        archive_url = Archivetodaystandard + tweet_url
    tweet_toprow = subprocess.run('grep -n -m 1 \'<div class="tweet-content media-body" dir="auto">\' | cut -f 1 --delim=\':\'',input=tweet_list.encode('utf-8'),stdout=subprocess.PIPE,shell=True).stdout.decode('utf-8').replace('\n','')
    tweet_bottomrow = subprocess.run('tail -n +' + tweet_toprow + ' | grep -n -m 1 "</div>" | cut -f 1 --delim=\':\'',input=tweet_list.encode('utf-8'),stdout=subprocess.PIPE,shell=True).stdout.decode('utf-8').replace('\n','')
    tweet_content = subprocess.run('tail -n +' + tweet_toprow + ' | head -n ' + tweet_bottomrow + ' | sed \'s/^.*<div class="tweet-content media-body" dir="auto">//\' | sed \'s#</div>.*$##\' | sed \'s#<a href="/[^<]*>##g\' | sed -r \'s#<a href="(http[^"]*)">[^<]*</a>*#\\n{{Archive|1=\\1|2=' + Archivetoday + '\\1|3=\\1}}#g\' | sed \'s#</a>##g\' | sed \'s/$/<br>/\' | sed \'$ s/<br>//\'',input=tweet_list.encode('utf-8'),stdout=subprocess.PIPE,shell=True).stdout.decode('utf-8').replace('#','<nowiki>#</nowiki>').rstrip()
    for archive_link in re.findall(Archivetoday + 'http[^|]*',tweet_content):
        src_url = archive_link.replace(Archivetoday,'')
        archive_redirectedlink = Archivetoday + get_redirect_url(src_url)
        archive_new = subprocess.run(Curlhead + '"' + archive_redirectedlink + '" | sed \'s/>/>\\n/g\'| grep -m 1 "display:block;color:blue;font-size:16px;word-break:break-word" | sed \'s/.*display:block;color:blue;font-size:16px;word-break:break-word" href="//\' | cut -f 1 --delim=\'"\' | sed \'s#https\\{0,1\\}://[^/]*/#' + Archivetodaystandard + '#\'',stdout=subprocess.PIPE,shell=True).stdout.decode('utf-8').replace('\n','')
        if archive_new == '':
            print(archive_link.replace(Archivetoday,'') + "の魚拓がない。これはいけない。")
            archive_new = archive_link.replace(Archivetoday,Archivetodaystandard)
        tweet_content = tweet_content.replace(archive_link,archive_new)
    output_txt = '!{{CallinShowLink|1=' + tweet_url + '|2=' + archive_url + '}}\n|-\n|\n' + tweet_content + '\n|-\n' + output_txt
    print("ツイートを" + str(i + 1) + "件も記録したンゴwwwwwwwwwww")
    tweet_list = tweet_list.replace(HTMLtext, '済', 1)
    if not end_blank and end_str in tweet_content:
        print("目的ツイート発見でもう尾張屋根")
        break
else:
    print(str(Limit) + "件も記録している。もうやめにしませんか。")
make_txt(output_txt)
-->
<!--
#!/usr/bin/env python3
 
 
#当コードは恒心停止してしまったhttps://rentry.co/7298gの降臨ショーツイート自動収集スクリプトの復刻改善版です
#前開発者との出会いに感謝
#
#ーーーーーーーー【注意点】ーーーーーーーーーー
#・玉葱を使用すること。
#・バグ報告はhttps://krsw-wiki.org/wiki/?curid=15799。


ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
'''


#インポート類
#インポート類
import subprocess
import sys
import codecs
import codecs
import re
import re
179行目: 53行目:
##記事の文章に使うのはクリアネット
##記事の文章に使うのはクリアネット
Archivetodaystandard = 'https://archive.ph/'
Archivetodaystandard = 'https://archive.ph/'
##twitterのURL
Twitterurl = 'https://twitter.com/'


##降臨ショーのユーザーネーム
##降臨ショーのユーザーネーム
187行目: 64行目:


##取得するツイート数の上限
##取得するツイート数の上限
Limit_tweet: Final[int] = 400
Limit_tweet: Final[int] = 100


##HTTPリクエスト失敗時の再試行回数
##HTTPリクエスト失敗時の再試行回数
193行目: 70行目:


##HTTPリクエスト失敗時の待機時間
##HTTPリクエスト失敗時の待機時間
Waittime: Final[int] = 5
Waittime_error: Final[int] = 5
 
##HTTPリクエスト成功時の待機時間
Waittime_success: Final[int] = 1


##nitterのURLのドメインとユーザーネーム部分の接続部品
##nitterのURLのドメインとユーザーネーム部分の接続部品
Searchquery = "search?q=from%3A"
Searchquery: Final[str] = "search?q=from%3A"


##HTTPリクエスト時のユーザーエージェントとヘッダ
##HTTPリクエスト時のユーザーエージェントとヘッダ
user_agent = 'Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0'
user_agent: Final[str] = 'Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0'
header = {
header = {
     'User-Agent': user_agent
     'User-Agent': user_agent
206行目: 86行目:
##nitterでユーザーがいなかったとき返ってくるページのタイトル
##nitterでユーザーがいなかったとき返ってくるページのタイトル
##万が一仕様変更で変わったとき用
##万が一仕様変更で変わったとき用
Nitter_error_title = "Error|nitter"
Nitter_error_title: Final[str] = "Error|nitter"
 
##archive.todayで魚拓がなかったときのレスポンス
##万が一仕様変更で変わったとき用
Noarchive: Final[str] = "No results"


Curlhead = 'torsocks curl -sS -A "Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0" '
##nitterで次ページ遷移時のURLから抜け落ちてる部分
Redirectcurl = ' -w "%{redirect_url}" -o /dev/null 2> /dev/null'
Search: Final[str] = "search"
Searchquery = "search?q=from%3A"
Andamp = '&' + 'amp;'
HTMLtext = '<div class="tweet-content media-body" dir="auto">'


##nitterの前ページ読み込み部分の名前
##万が一仕様変更で変わったとき用
Newest: Final[str] = "Load newest"
#関数類
##坂根輝美に場所を知らせます
##坂根輝美に場所を知らせます
def pickupcounter():
def pickupcounter():
220行目: 106行目:
##引数のURLにHTTP接続します
##引数のURLにHTTP接続します
##失敗かどうかは呼出側で要判定
##失敗かどうかは呼出側で要判定
def request_onetime(url: Final[str]) -> Response:
def request_onetime(url: Final[str]) -> requests.models.Response:
return requests.get(url, timeout=Request_timeout, headers=header)
return requests.get(url, timeout=Request_timeout, headers=header,allow_redirects=False)


##HTTP接続を再試行回数まで試します
##HTTP接続を再試行回数まで試します
##成功すると結果を返します
##成功すると結果を返します
##接続失敗が何度も起きるとエラーと今までにとれたツイートを吐き出して終了
##接続失敗が何度も起きるとNoneを返します
def request(url: Final[str]) -> Response:
##呼出側で要None判定
def request(url: Final[str]) -> requests.models.Response:
counter = 1
counter = 1
while True:
while True:
234行目: 121行目:
except requests.exceptions.RequestException as e:
except requests.exceptions.RequestException as e:
if counter <= Limit_request:
if counter <= Limit_request:
print(url + 'への通信失敗ナリ' + str(counter) + '/' + str(Limit_request) + '回')
print(url + 'への通信失敗ナリ ' + str(counter) + '/' + str(Limit_request) + '回')
counter += 1
counter += 1
sleep(Waittime)
sleep(Waittime_error)
else:
else:
print('通信失敗しすぎで強制終了ナリ')
return None
make_txt(txt_data)
exit()
else:
else:
return res
return res


##URLの最後にスラッシュ付いていなければ付ける
##URLの最後にスラッシュ付いていなければ付ける
##Twitterだけスラッシュ付いていないほうが都合いいので抜く
def slash_check():
def slash_check():
global Nitterinstance
global Archivetoday
global Archivetodaystandard
global Twitterurl
if Nitterinstance[-1] != '/':
if Nitterinstance[-1] != '/':
Nitterinstance = Nitterinstance + '/'
Nitterinstance = Nitterinstance + '/'
252行目: 142行目:
if Archivetodaystandard[-1] != '/':
if Archivetodaystandard[-1] != '/':
Archivetodaystandard = Archivetodaystandard + '/'
Archivetodaystandard = Archivetodaystandard + '/'
if Twitterurl[-1] == '/':
Twitterurl = Twitterurl[0:len(Twitterurl)-1]


##nitterのインスタンスが生きているかチェック
##nitterのインスタンスが生きているかチェック
258行目: 150行目:
##激重インスタンスが指定されたとき試行回数増やして偶然成功してそのまま実行されるのを躱すため
##激重インスタンスが指定されたとき試行回数増やして偶然成功してそのまま実行されるのを躱すため
def instance_check():
def instance_check():
print("nitterのインスタンスチェック中ですを")
try:
try:
res = request_onetime(Nitterinstance) ##リクエスト
res = request_onetime(Nitterinstance) ##リクエスト
res.raise_for_status() ##HTTPステータスコードが200番台以外でエラー発生
res.raise_for_status() ##HTTPステータスコードが200番台以外でエラー発生
sleep(Waittime_success)
except requests.exceptions.RequestException as e: ##エラー発生時は終了
except requests.exceptions.RequestException as e: ##エラー発生時は終了
print('インスタンスが死んでますを')
print('インスタンスが死んでますを')
276行目: 170行目:
else:
else:
res = request(Nitterinstance + account_str) ##リクエストして結果取得
res = request(Nitterinstance + account_str) ##リクエストして結果取得
soup = bs4(res.text, 'html.parser') ##レスポンス解析
if res is None : ##リクエスト失敗判定
title_text = soup.find('title').get_text()
fail()
##1件もないときはtitleにエラー
sleep(Waittime_success)
if title_text == Nitter_error_title:
soup = bs4(res.text, 'html.parser') ##beautifulsoupでレスポンス解析
print(account_str + "は実在の人物ではありませんでした")
if soup.title == Nitter_error_title: ##タイトルがエラーでないか判定
print(account_str + "は実在の人物ではありませんでした") ##エラー時ループに戻る
else:
else:
print("最終的に出会ったのが@" + account_str + "だった。")
print("最終的に出会ったのが@" + account_str + "だった。")
return account_str
return account_str ##成功時アカウント名返す
##検索クエリを取得
##検索クエリを取得
def get_query(name: Final[str]) -> Response:
def get_query(name: Final[str]) -> requests.models.Response:
while True:
while True:
print("検索クエリを入れるナリ。複数ある時は一つの塊ごとに入力するナリ。空欄を入れると検索開始ナリ。")
print("検索クエリを入れるナリ。複数ある時は一つの塊ごとに入力するナリ。空欄を入れると検索開始ナリ。")
297行目: 192行目:
query_input = input()
query_input = input()
res = request(Nitterinstance + Searchquery + '+'.join(query_str)) ##リクエストして結果取得
res = request(Nitterinstance + Searchquery + '+'.join(query_str)) ##リクエストして結果取得
soup = bs4(res.text, 'html.parser') ##レスポンス解析
if res is None : ##リクエスト失敗判定
fail()
sleep(Waittime_success)
soup = bs4(res.text, 'html.parser') ##beautifulsoupでレスポンス解析
##1件もないときはHTMLにtimeline-noneがあるから判定
##1件もないときはHTMLにtimeline-noneがあるから判定
if soup.find(class_='timeline-none') is None:
if soup.find(class_='timeline-none') is None:
print("クエリのピースが埋まっていく。")
return res ##成功したリクエストのレスポンスを返す
else:
print("適切なクエリを入力することを切に望む。")
print("適切なクエリを入力することを切に望む。")
else:
 
print("クエリのピースが埋まっていく。")
##接続失敗時処理
return res
def fail():
print("接続失敗しすぎで強制終了ナリ")
if txt_data != '': ##取得成功したデータがあれば発行
print("取得成功した分だけ発行しますを")
make_txt()
exit() ##終了


##テキスト発行
##テキスト発行
def make_txt(txt_data):
def make_txt():
txt_data = '{|class="wikitable" style="text-align: left;"\n' + txt_data ##wikiの表の最初
global txt_data
txt_data = txt_data + '|}' ##wikiの表の最後
txt_data = '{|class="wikitable" style="text-align: left;"\n' + txt_data + '|}' ##wikiの表の最初と最後
##ファイル出力
##ファイル出力
with codecs.open('tweet.txt', 'w', 'utf-8') as f:
with codecs.open('tweet.txt', 'w', 'utf-8') as f:
316行目: 222行目:


##記録を中断するツイート
##記録を中断するツイート
def stop_txt() -> str:
def stop_word() -> str:
print("ここにツイートの本文を入れる!すると・・・・なんと一致するツイートで記録を中断する!(これは本当の仕様です。)ちなみに、空欄だと" + str(Limit_tweet) + "件まで終了しない。")
print("ここにツイートの本文を入れる!すると・・・・なんと一致するツイートで記録を中断する!(これは本当の仕様です。)ちなみに、空欄だと" + str(Limit_tweet) + "件まで終了しない。")
end_str = input() ##ユーザー入力受付
end_str = input() ##ユーザー入力受付
return end_str
return end_str


#ページからツイート本文をtxt_dataに収めていく
def get_tweet(page: Final[requests.models.Response],stop: Final[str]):
global txt_data
global limitcount
soup = bs4(page.text, 'html.parser') ##beautifulsoupでレスポンス解析
tweets = soup.find_all(class_='timeline-item') ##一ツイートのブロックごとにリストで取得
for tweet in tweets: ##一ツイート毎に処理
doublesoup = bs4(str(tweet) , 'html.parser') ##ツイートをさらに解析
if doublesoup.a.text == Newest: ##Load Newestのボタンは処理しない
continue
if not doublesoup.find(class_='retweet-header') is None: ##retweet-headerはリツイートを示すので入っていれば処理しない
continue
tweet_url = Twitterurl + re.sub('#[^#]*$','',doublesoup.find(class_='tweet-link').get('href')) ##ツイートのURL作成
archived_tweet_url = archiveurl(tweet_url,tweet_url) ##ツイートURLをテンプレートArchiveに変化
tweet_content = doublesoup.find(class_='tweet-content media-body') ##ツイートの中身だけ取り出す
triplesoup = bs4(str(tweet_content) , 'html.parser') ##URLの魚拓化のためにさらに解析
archivesoup(triplesoup) ##ツイートの中身のリンクをテンプレートArchiveに変化
txt_data = '!' + archived_tweet_url + '\n|-\n|\n' + triplesoup.get_text().replace('\n','<br>\n').replace('#','<nowiki>#</nowiki>') + '\n|-\n' + txt_data ##wikiの文法に変化
limitcount += 1 ##記録回数をカウント
print("ツイートを" + str(limitcount) + "件も記録したンゴwwwwwwwwwww")
if stop != '' and stop in triplesoup.get_text(): ##目的ツイートか判定
print("目的ツイート発見でもう尾張屋根")
make_txt()
if limitcount >= Limit_tweet: ##上限達成か判定
print(str(Limit_tweet) + "件も記録している。もうやめにしませんか。")
make_txt()
#soupをテンプレートArchiveに変化させる
def archivesoup(soup):
urls_in_tweet = soup.find_all('a')
for url in urls_in_tweet:
if not re.match('^https?://',url.get('href')) is None:
newstr = soup.new_string(archiveurl(url.get('href'),url.text)) ##テンプレートArchiveの文字列作成
url.replace_with(newstr) ##テンプレートArchiveに変化
#URLをテンプレートArchiveに変化させる
def archiveurl(url: Final[str],text: Final[str]) -> str:
return '{{Archive|1=' + url + '|2=' + archive(url) + '|3=' + text  + '}}' ##テンプレートArchiveの文字列返す
##URLから魚拓返す
def archive(url: Final[str]) -> str:
archive_url = Archivetodaystandard + url
res = request(Archivetoday + url)
if res is None : ##魚拓接続失敗時処理
print(archive_url + 'にアクセス失敗ナリ。出力されるテキストにはそのまま記載されるナリ。')
else:
sleep(Waittime_success)
soup = bs4(res.text, 'html.parser') ##beautifulsoupでレスポンス解析
content = soup.find(id="CONTENT").text ##archive.todayの魚拓一覧ページの中身だけ取得
if content[:len(Noarchive)] == Noarchive:
print(url + "の魚拓がない。これはいけない。")
else:
doublesoup = bs4(res.text, 'html.parser') ##beautifulsoupでレスポンス解析
archive_url = soup.find('a').get('href').replace(Archivetoday,Archivetodaystandard)
return archive_url
##新しいページを取得
def getnewpage(page: Final[requests.models.Response]) -> requests.models.Response:
soup = bs4(page.text, 'html.parser') ##beautifulsoupでレスポンス解析
showmores = soup.find_all(class_="show-more")
for showmore in showmores: ##show-moreに次ページへのリンクか前ページへのリンクがある
if showmore.text != Newest:  ##前ページへのリンクではないか判定
newurl = Nitterinstance + Search + showmore.a.get('href') ##直下のaタグのhrefの中身取ってURL頭部分と合体
res = request(newurl)
if res is None:
fail()
sleep(Waittime_success)
newsoup = bs4(res.text, 'html.parser')
if newsoup.find(class_="timeline-end") is None:
print(res.url + 'に移動しますを')
return res
else:
print("急に残りツイートが無くなったな終了するか")
make_txt()
##メイン
txt_data = '' ##出力するデータ
limitcount = 0 ##記録数
krsw=False
if len(sys.argv) > 1 and sys.argv[1] == 'krsw':
krsw=True
slash_check() ##スラッシュが抜けてないかチェック
instance_check() ##インスタンスが死んでないかチェック
##ユーザー名取得
if krsw:
print('名前は自動的に' + Callinshow + 'にナリます')
name = Callinshow
else:
name = get_name()
##検索クエリとページ取得
if krsw:
print('クエリは自動的になしにナリます')
page = request(Nitterinstance + Searchquery + name)
if page is None:
fail()
else:
page = get_query(name)


slash_check()
##終わりにするツイート取得
instance_check()
if krsw:
name = get_name()
print('終わりにするツイートは自動的になしにナリます')
page = get_query(name)
stop = ''
stop = stop_txt()
else:
stop = stop_word()
 
##ツイートを取得し終えるまでループ
while True:
while True:
get_tweet(page,stop) ##txt_dataにページ内のリツイート以外の全ツイートの中身突っ込んでいく
page = getnewpage(page) ##新しいページ取得
-->
‎</syntaxhighlight>
== 実行例 ==
20件での実行例。
{|class="wikitable" style="text-align: left;"
!{{Archive|1=https://twitter.com/CallinShow/status/1536743227730333696|2=http://archivecaslytosk.onion/|3=https://twitter.com/CallinShow/status/1536743227730333696}}
|-
|
弁護士唐澤貴洋への匿名のメッセージを募集中!<br>
<nowiki>#</nowiki>マシュマロを投げ合おう<br>
{{Archive|1=https://marshmallow-qa.com/apt/13e40311-bdb3-47b6-a0bd-f989f20cd603|2=https://archive.ph/https://marshmallow-qa.com/apt/13e40311-bdb3-47b6-a0bd-f989f20cd603|3=marshmallow-qa.com/apt/13e40…}}
|-
!{{Archive|1=https://twitter.com/CallinShow/status/1537362287949819904|2=http://archivecaslytosk.onion/|3=https://twitter.com/CallinShow/status/1537362287949819904}}
|-
|
弁護士唐澤貴洋への匿名のメッセージを募集中!<br>
<nowiki>#</nowiki>マシュマロを投げ合おう<br>
{{Archive|1=https://marshmallow-qa.com/apt/a03fc481-95a5-4714-861f-3bf4d060b3ce|2=https://archive.ph/https://marshmallow-qa.com/apt/a03fc481-95a5-4714-861f-3bf4d060b3ce|3=marshmallow-qa.com/apt/a03fc…}}
|-
!{{Archive|1=https://twitter.com/CallinShow/status/1537752697361027072|2=http://archivecaslytosk.onion/|3=https://twitter.com/CallinShow/status/1537752697361027072}}
|-
|
菊地さん、会社の財務諸表を年末だけではなく、来週でも開示してください。<br>
<br>
事前相談実施のプレスリリースって初めて見たな。
|-
!{{Archive|1=https://twitter.com/CallinShow/status/1537754176939528192|2=http://archivecaslytosk.onion/|3=https://twitter.com/CallinShow/status/1537754176939528192}}
|-
|
オレはオレで、オレのところに来た声を届けるからな。<br>
<br>
第二回目の相談のプレスリリースあるのか。
|-
!{{Archive|1=https://twitter.com/CallinShow/status/1537795453110333440|2=http://archivecaslytosk.onion/|3=https://twitter.com/CallinShow/status/1537795453110333440}}
|-
|
テレビに真実はあるのか。<br>
<br>
言いたいことが言えないテレ朝もどうなんだろうな。
|-
!{{Archive|1=https://twitter.com/CallinShow/status/1537795802302918656|2=http://archivecaslytosk.onion/|3=https://twitter.com/CallinShow/status/1537795802302918656}}
|-
|
テレビ局なんか既得権益だろ。<br>
<br>
在野の声をSNSを使って行っていくしかない。
|-
!{{Archive|1=https://twitter.com/CallinShow/status/1537796046780497920|2=http://archivecaslytosk.onion/|3=https://twitter.com/CallinShow/status/1537796046780497920}}
|-
|
オレたちはもう騙されないんだ。<br>
<br>
全てを変えるんだ。
|-
!{{Archive|1=https://twitter.com/CallinShow/status/1537796546263416832|2=http://archivecaslytosk.onion/|3=https://twitter.com/CallinShow/status/1537796546263416832}}
|-
|
なあ、この国は良くなってるのか。<br>
<br>
若い人は安く使われ、<br>
物価は上がり、<br>
公共事業だけはお金を垂れ流していく。<br>
少子化が進み、年金も当てにならない。<br>
<br>
何でこんなに若い人が苦しまないといけないんだ。
|-
!{{Archive|1=https://twitter.com/CallinShow/status/1537796983578333184|2=http://archivecaslytosk.onion/|3=https://twitter.com/CallinShow/status/1537796983578333184}}
|-
|
若い人が全員選挙行ったらこの国は変わるぜ。<br>
<br>
ガチャのチケットもってるのに使わない手はないだろ。
|-
!{{Archive|1=https://twitter.com/CallinShow/status/1537797605308395520|2=http://archivecaslytosk.onion/|3=https://twitter.com/CallinShow/status/1537797605308395520}}
|-
|
👍
|-
!{{Archive|1=https://twitter.com/CallinShow/status/1537798386619133952|2=http://archivecaslytosk.onion/|3=https://twitter.com/CallinShow/status/1537798386619133952}}
|-
|
頼むよ!君にかかってる。
|-
!{{Archive|1=https://twitter.com/CallinShow/status/1537799090326863872|2=http://archivecaslytosk.onion/|3=https://twitter.com/CallinShow/status/1537799090326863872}}
|-
|
引きこもりの人は引きこもりになりたくてなったんじゃない。<br>
苦しんでるんだ。<br>
誰かがその声を聞く必要がある。<br>
強制的に連れ出すとかやばいだろ。<br>
法規制する必要あるでしょ。<br>
強要罪だぜ。<br>
全ては見過ごされてるんだよ。
|-
!{{Archive|1=https://twitter.com/CallinShow/status/1537803303752388608|2=http://archivecaslytosk.onion/|3=https://twitter.com/CallinShow/status/1537803303752388608}}
|-
|
この国は<br>
<br>
やばいだろ。<br>
<br>
引用リスペクト 梅野源治選手
|-
!{{Archive|1=https://twitter.com/CallinShow/status/1538070416488792064|2=http://archivecaslytosk.onion/|3=https://twitter.com/CallinShow/status/1538070416488792064}}
|-
|
本当、そうだと思う。<br>
<br>
貴重な意見ありがとう。
|-
!{{Archive|1=https://twitter.com/CallinShow/status/1538070983827165186|2=http://archivecaslytosk.onion/|3=https://twitter.com/CallinShow/status/1538070983827165186}}
|-
|
この国は円安が進み、外国旅行に行ったことのない若者も多く。<br>
どうなるんだ。<br>
<br>
ライジングサンってどうなってるんだ。<br>
<br>
今は戦後の混乱期と同じだ。<br>
<br>
若い人たちで国を作るんだ。
|-
!{{Archive|1=https://twitter.com/CallinShow/status/1538071752802467840|2=http://archivecaslytosk.onion/|3=https://twitter.com/CallinShow/status/1538071752802467840}}
|-
|
立花先生は、いつも一緒懸命だ。<br>
<br>
信念は変わらない。<br>
<br>
理不尽との闘い。<br>
<br>
立花先生が裁判を起こすことは至極まともなことだ。<br>
<br>
だって最後は司法なはずだろ。<br>
<br>
でも、裁判は、ひどい判決も多い。<br>
<br>
権力に擦り寄ることもある。<br>
<br>
そんな全てを曝け出しているのが立花先生だ。<br>
<br>
こんな人今までいたか。
|-
!{{Archive|1=https://twitter.com/CallinShow/status/1538072997734793216|2=http://archivecaslytosk.onion/|3=https://twitter.com/CallinShow/status/1538072997734793216}}
|-
|
立花先生はいつも現場主義。<br>
<br>
人々の声を拾い。<br>
<br>
それを手練手管で伝えいく。<br>
<br>
時に道化を案じるのは、みんなに問題を気づいてほしいからだ。<br>
<br>
この3年間、少数政党でこれだけ目立ち続けた政党があるか。<br>
<br>
それは全て、NHKを変えたい、人々に良くなってもらいたい。<br>
<br>
その一心でしかないと思う。
|-
!{{Archive|1=https://twitter.com/CallinShow/status/1538073614540754944|2=http://archivecaslytosk.onion/|3=https://twitter.com/CallinShow/status/1538073614540754944}}
|-
|
全てをめくっていくんだよ。<br>
<br>
ガーシーさんは、その第一章だ。<br>
<br>
許せないことを言うことは大切だと。<br>
<br>
ガーシー党は、言わば情報公開政党ってことだろ。<br>
<br>
いいじゃないか、澱んだ永田町が少しは澄んでくるんじゃないか。
|-
!{{Archive|1=https://twitter.com/CallinShow/status/1538074524721491968|2=http://archivecaslytosk.onion/|3=https://twitter.com/CallinShow/status/1538074524721491968}}
|-
|
{{Archive|1=https://piped.kavin.rocks/nrJqzmMWUCU|2=https://archive.ph/https://piped.kavin.rocks/nrJqzmMWUCU|3=piped.kavin.rocks/nrJqzmMWUCU}}<br>
<br>
澱んだ街ごと食い荒らすんだ。
|-
!{{Archive|1=https://twitter.com/CallinShow/status/1538582552423780352|2=https://archive.ph/https://twitter.com/CallinShow/status/1538582552423780352|3=https://twitter.com/CallinShow/status/1538582552423780352}}
|-
|
弁護士唐澤貴洋への匿名のメッセージを募集中!<br>
<nowiki>#</nowiki>マシュマロを投げ合おう<br>
{{Archive|1=https://marshmallow-qa.com/apt/21e00e4b-e468-42e9-bd14-44c2bf9b37d0|2=https://archive.ph/https://marshmallow-qa.com/apt/21e00e4b-e468-42e9-bd14-44c2bf9b37d0|3=marshmallow-qa.com/apt/21e00…}}
|-
|}

2022年6月20日 (月) 05:08時点における版

‎とりあえず取り急ぎ。バグ報告は利用者・トーク:夜泣き

コード

#!/usr/bin/env python3

'''
当コードは恒心停止してしまったhttps://rentry.co/7298gの降臨ショーツイート自動収集スクリプトの復刻改善版です
前開発者との出会いに感謝

〜〜〜〜〜〜〜〜〜〜〜〜〜【使い方】〜〜〜〜〜〜〜〜〜〜〜〜〜

・terminalに $ python3 (ファイル名) で作動します
・定数類は状況に応じて変えてください
・$ python3 (ファイル名) krsw コマンドライン引数をkrswとつけると自動モードです
・自動モードではユーザーは降臨ショー、クエリはなし、取得上限まで自動です
・つまりユーザー入力が要りません

〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜

ーーーーーーーーーーーーー【注意点】ーーーーーーーーーーーーー

・環境は玉葱前提です。
・Whonix-Workstationで動作確認済
・bs4はインストールしないと標準で入ってません
・requestsも環境によっては入っていないかもしれない
・$ pip install bs4 requests
・pipも入っていなければ$ sudo apt install pip
・バグ報告はhttps://krsw-wiki.org/wiki/?curid=15799

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
'''

#インポート類
import sys
import codecs
import re
from time import sleep
import requests
from bs4 import BeautifulSoup as bs4
from typing import Final
from urllib.parse import quote

#定数類
##nitterのインスタンス
##生きているのはhttps://github.com/zedeus/nitter/wiki/Instancesで確認
Nitterinstance = 'http://nitterqdyumlovt7tjqpdjrluitgmtpa53qq3idlpgoe4kxo7gs3xvad.onion/'

##archive.todayの魚拓
##実際にアクセスして魚拓があるか調べるのにはonionを使用
Archivetoday  = 'http://archiveiya74codqgiixo33q62qlrqtkgmcitqx5u2oeqnmn5bpcbiyd.onion/'

##archive.todayの魚拓
##記事の文章に使うのはクリアネット
Archivetodaystandard = 'https://archive.ph/'

##twitterのURL
Twitterurl = 'https://twitter.com/'

##降臨ショーのユーザーネーム
Callinshow: Final[str] = 'CallinShow'

##HTTPリクエストのタイムアウト秒数
Request_timeout: Final[int] = 30

##取得するツイート数の上限
Limit_tweet: Final[int] = 100

##HTTPリクエスト失敗時の再試行回数
Limit_request: Final[int] = 5

##HTTPリクエスト失敗時の待機時間
Waittime_error: Final[int] = 5

##HTTPリクエスト成功時の待機時間
Waittime_success: Final[int] = 1

##nitterのURLのドメインとユーザーネーム部分の接続部品
Searchquery: Final[str] = "search?q=from%3A"

##HTTPリクエスト時のユーザーエージェントとヘッダ
user_agent: Final[str] = 'Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0'
header = {
    'User-Agent': user_agent
}

##nitterでユーザーがいなかったとき返ってくるページのタイトル
##万が一仕様変更で変わったとき用
Nitter_error_title: Final[str] = "Error|nitter"

##archive.todayで魚拓がなかったときのレスポンス
##万が一仕様変更で変わったとき用
Noarchive: Final[str] = "No results"

##nitterで次ページ遷移時のURLから抜け落ちてる部分
Search: Final[str] = "search"

##nitterの前ページ読み込み部分の名前
##万が一仕様変更で変わったとき用
Newest: Final[str] = "Load newest"

#関数類
##坂根輝美に場所を知らせます
def pickupcounter():
	print('ピックアップカウンター付近でふ')
	
##引数のURLにHTTP接続します
##失敗かどうかは呼出側で要判定
def request_onetime(url: Final[str]) -> requests.models.Response:
	return requests.get(url, timeout=Request_timeout, headers=header,allow_redirects=False)

##HTTP接続を再試行回数まで試します
##成功すると結果を返します
##接続失敗が何度も起きるとNoneを返します
##呼出側で要None判定
def request(url: Final[str]) -> requests.models.Response:
	counter = 1
	while True:
		try:
			res = request_onetime(url)
			res.raise_for_status()
		except requests.exceptions.RequestException as e:
			if counter <= Limit_request:
				print(url + 'への通信失敗ナリ  ' + str(counter) + '/' + str(Limit_request) + '回')
				counter += 1
				sleep(Waittime_error)
			else:
				return None
		else:
			return res

##URLの最後にスラッシュ付いていなければ付ける
##Twitterだけスラッシュ付いていないほうが都合いいので抜く
def slash_check():
	global Nitterinstance
	global Archivetoday
	global Archivetodaystandard
	global Twitterurl
	if Nitterinstance[-1] != '/':
		Nitterinstance = Nitterinstance + '/'
	if Archivetoday[-1] != '/':
		Archivetoday = Archivetoday + '/'
	if Archivetodaystandard[-1] != '/':
		Archivetodaystandard = Archivetodaystandard + '/'
	if Twitterurl[-1] == '/':
		Twitterurl = Twitterurl[0:len(Twitterurl)-1]

##nitterのインスタンスが生きているかチェック
##死んでいたらそこで終了
##接続を一回しか試さないrequest_onetimeを使っているのは
##激重インスタンスが指定されたとき試行回数増やして偶然成功してそのまま実行されるのを躱すため
def instance_check():
	print("nitterのインスタンスチェック中ですを")
	try:
		res = request_onetime(Nitterinstance) ##リクエスト
		res.raise_for_status() ##HTTPステータスコードが200番台以外でエラー発生
		sleep(Waittime_success)
	except requests.exceptions.RequestException as e: ##エラー発生時は終了
		print('インスタンスが死んでますを')
		exit()

##ツイート収集するユーザー名を取得
##何も入力しないと尊師を指定するよう改良
def get_name() -> str:
	while True:
		print('アカウント名を入れなければない。空白だと自動的に' + Callinshow + 'になりますを')
		account_str = input() ##ユーザー入力受付
		##空欄で降臨ショー
		if account_str == '':
			return Callinshow
		else:
			res = request(Nitterinstance + account_str) ##リクエストして結果取得
			if res is None : ##リクエスト失敗判定
				fail()
			sleep(Waittime_success)
			soup = bs4(res.text, 'html.parser') ##beautifulsoupでレスポンス解析
			if soup.title == Nitter_error_title: ##タイトルがエラーでないか判定
				print(account_str + "は実在の人物ではありませんでした") ##エラー時ループに戻る
			else:
				print("最終的に出会ったのが@" + account_str + "だった。")
				return account_str ##成功時アカウント名返す
				
##検索クエリを取得
def get_query(name: Final[str]) -> requests.models.Response:
	while True:
		print("検索クエリを入れるナリ。複数ある時は一つの塊ごとに入力するナリ。空欄を入れると検索開始ナリ。")
		print("例:「無能」→改行→「脱糞」→改行→「弟殺し」→改行→空欄で改行")
		query_str = [name] ##検索クエリ文字列をquery_strに収納。ユーザー名を先に含めておく
		query_input = input() ##ユーザー入力受付
		##空欄が押されるまでユーザー入力受付
		while query_input != '':
			query_str.append(quote(query_input))
			query_input = input()
		res = request(Nitterinstance + Searchquery + '+'.join(query_str)) ##リクエストして結果取得
		if res is None : ##リクエスト失敗判定
			fail()
		sleep(Waittime_success)
		soup = bs4(res.text, 'html.parser') ##beautifulsoupでレスポンス解析
		##1件もないときはHTMLにtimeline-noneがあるから判定
		if soup.find(class_='timeline-none') is None:
			print("クエリのピースが埋まっていく。")
			return res ##成功したリクエストのレスポンスを返す
		else:
			print("適切なクエリを入力することを切に望む。")

##接続失敗時処理
def fail():
	print("接続失敗しすぎで強制終了ナリ")
	if txt_data != '': ##取得成功したデータがあれば発行
		print("取得成功した分だけ発行しますを")
		make_txt()
	exit() ##終了

##テキスト発行
def make_txt():
	global txt_data
	txt_data = '{|class="wikitable" style="text-align: left;"\n' + txt_data + '|}' ##wikiの表の最初と最後
	##ファイル出力
	with codecs.open('tweet.txt', 'w', 'utf-8') as f:
		f.write(txt_data)
	print("テキストファイル手に入ったやで〜")
	exit() ##終了

##記録を中断するツイート
def stop_word() -> str:
	print("ここにツイートの本文を入れる!すると・・・・なんと一致するツイートで記録を中断する!(これは本当の仕様です。)ちなみに、空欄だと" + str(Limit_tweet) + "件まで終了しない。")
	end_str = input() ##ユーザー入力受付
	return end_str

#ページからツイート本文をtxt_dataに収めていく
def get_tweet(page: Final[requests.models.Response],stop: Final[str]):
	global txt_data
	global limitcount
	soup = bs4(page.text, 'html.parser') ##beautifulsoupでレスポンス解析
	tweets = soup.find_all(class_='timeline-item') ##一ツイートのブロックごとにリストで取得
	for tweet in tweets: ##一ツイート毎に処理
		doublesoup = bs4(str(tweet) , 'html.parser') ##ツイートをさらに解析
		if doublesoup.a.text == Newest: ##Load Newestのボタンは処理しない
			continue
		if not doublesoup.find(class_='retweet-header') is None: ##retweet-headerはリツイートを示すので入っていれば処理しない
			continue
		tweet_url = Twitterurl + re.sub('#[^#]*$','',doublesoup.find(class_='tweet-link').get('href')) ##ツイートのURL作成
		archived_tweet_url = archiveurl(tweet_url,tweet_url) ##ツイートURLをテンプレートArchiveに変化
		tweet_content = doublesoup.find(class_='tweet-content media-body') ##ツイートの中身だけ取り出す
		triplesoup = bs4(str(tweet_content) , 'html.parser') ##URLの魚拓化のためにさらに解析
		archivesoup(triplesoup) ##ツイートの中身のリンクをテンプレートArchiveに変化
		txt_data = '!' + archived_tweet_url + '\n|-\n|\n' + triplesoup.get_text().replace('\n','<br>\n').replace('#','<nowiki>#</nowiki>') + '\n|-\n' + txt_data ##wikiの文法に変化
		limitcount += 1 ##記録回数をカウント
		print("ツイートを" + str(limitcount) + "件も記録したンゴwwwwwwwwwww")
		if stop != '' and stop in triplesoup.get_text(): ##目的ツイートか判定
			print("目的ツイート発見でもう尾張屋根")
			make_txt()
		if limitcount >= Limit_tweet: ##上限達成か判定
			print(str(Limit_tweet) + "件も記録している。もうやめにしませんか。")
			make_txt()

#soupをテンプレートArchiveに変化させる
def archivesoup(soup):
	urls_in_tweet = soup.find_all('a')
	for url in urls_in_tweet:
		if not re.match('^https?://',url.get('href')) is None:
			newstr = soup.new_string(archiveurl(url.get('href'),url.text)) ##テンプレートArchiveの文字列作成
			url.replace_with(newstr) ##テンプレートArchiveに変化

#URLをテンプレートArchiveに変化させる
def archiveurl(url: Final[str],text: Final[str]) -> str:
	return '{{Archive|1=' + url + '|2=' + archive(url) + '|3=' + text  + '}}' ##テンプレートArchiveの文字列返す

##URLから魚拓返す
def archive(url: Final[str]) -> str:
	archive_url = Archivetodaystandard + url
	res = request(Archivetoday + url)
	if res is None : ##魚拓接続失敗時処理
		print(archive_url + 'にアクセス失敗ナリ。出力されるテキストにはそのまま記載されるナリ。')
	else:
		sleep(Waittime_success)
		soup = bs4(res.text, 'html.parser') ##beautifulsoupでレスポンス解析
		content = soup.find(id="CONTENT").text ##archive.todayの魚拓一覧ページの中身だけ取得
		if content[:len(Noarchive)] == Noarchive:
			print(url + "の魚拓がない。これはいけない。")
		else:
			doublesoup = bs4(res.text, 'html.parser') ##beautifulsoupでレスポンス解析
			archive_url = soup.find('a').get('href').replace(Archivetoday,Archivetodaystandard)
	return archive_url

##新しいページを取得
def getnewpage(page: Final[requests.models.Response]) -> requests.models.Response:
	soup = bs4(page.text, 'html.parser') ##beautifulsoupでレスポンス解析
	showmores = soup.find_all(class_="show-more")
	for showmore in showmores: ##show-moreに次ページへのリンクか前ページへのリンクがある
		if showmore.text != Newest:  ##前ページへのリンクではないか判定
			newurl = Nitterinstance + Search + showmore.a.get('href') ##直下のaタグのhrefの中身取ってURL頭部分と合体
	res = request(newurl)
	if res is None:
		fail()
	sleep(Waittime_success)
	newsoup = bs4(res.text, 'html.parser')
	if newsoup.find(class_="timeline-end") is None:
		print(res.url + 'に移動しますを')
		return res
	else:
		print("急に残りツイートが無くなったな終了するか")
		make_txt()



##メイン
txt_data = '' ##出力するデータ
limitcount = 0 ##記録数
krsw=False
if len(sys.argv) > 1 and sys.argv[1] == 'krsw':
	krsw=True
slash_check() ##スラッシュが抜けてないかチェック
instance_check() ##インスタンスが死んでないかチェック

##ユーザー名取得
if krsw:
	print('名前は自動的に' + Callinshow + 'にナリます')
	name = Callinshow
else:
	name = get_name()

##検索クエリとページ取得
if krsw:
	print('クエリは自動的になしにナリます')
	page = request(Nitterinstance + Searchquery + name)
	if page is None:
		fail()
else:
	page = get_query(name)

##終わりにするツイート取得
if krsw:
	print('終わりにするツイートは自動的になしにナリます')
	stop = ''
else:
	stop = stop_word()

##ツイートを取得し終えるまでループ
while True:
	get_tweet(page,stop) ##txt_dataにページ内のリツイート以外の全ツイートの中身突っ込んでいく
	page = getnewpage(page) ##新しいページ取得

実行例

20件での実行例。

https://twitter.com/CallinShow/status/1536743227730333696(魚拓)

弁護士唐澤貴洋への匿名のメッセージを募集中!
#マシュマロを投げ合おう
marshmallow-qa.com/apt/13e40…(魚拓)

https://twitter.com/CallinShow/status/1537362287949819904(魚拓)

弁護士唐澤貴洋への匿名のメッセージを募集中!
#マシュマロを投げ合おう
marshmallow-qa.com/apt/a03fc…(魚拓)

https://twitter.com/CallinShow/status/1537752697361027072(魚拓)

菊地さん、会社の財務諸表を年末だけではなく、来週でも開示してください。

事前相談実施のプレスリリースって初めて見たな。

https://twitter.com/CallinShow/status/1537754176939528192(魚拓)

オレはオレで、オレのところに来た声を届けるからな。

第二回目の相談のプレスリリースあるのか。

https://twitter.com/CallinShow/status/1537795453110333440(魚拓)

テレビに真実はあるのか。

言いたいことが言えないテレ朝もどうなんだろうな。

https://twitter.com/CallinShow/status/1537795802302918656(魚拓)

テレビ局なんか既得権益だろ。

在野の声をSNSを使って行っていくしかない。

https://twitter.com/CallinShow/status/1537796046780497920(魚拓)

オレたちはもう騙されないんだ。

全てを変えるんだ。

https://twitter.com/CallinShow/status/1537796546263416832(魚拓)

なあ、この国は良くなってるのか。

若い人は安く使われ、
物価は上がり、
公共事業だけはお金を垂れ流していく。
少子化が進み、年金も当てにならない。

何でこんなに若い人が苦しまないといけないんだ。

https://twitter.com/CallinShow/status/1537796983578333184(魚拓)

若い人が全員選挙行ったらこの国は変わるぜ。

ガチャのチケットもってるのに使わない手はないだろ。

https://twitter.com/CallinShow/status/1537797605308395520(魚拓)

👍

https://twitter.com/CallinShow/status/1537798386619133952(魚拓)

頼むよ!君にかかってる。

https://twitter.com/CallinShow/status/1537799090326863872(魚拓)

引きこもりの人は引きこもりになりたくてなったんじゃない。
苦しんでるんだ。
誰かがその声を聞く必要がある。
強制的に連れ出すとかやばいだろ。
法規制する必要あるでしょ。
強要罪だぜ。
全ては見過ごされてるんだよ。

https://twitter.com/CallinShow/status/1537803303752388608(魚拓)

この国は

やばいだろ。

引用リスペクト 梅野源治選手

https://twitter.com/CallinShow/status/1538070416488792064(魚拓)

本当、そうだと思う。

貴重な意見ありがとう。

https://twitter.com/CallinShow/status/1538070983827165186(魚拓)

この国は円安が進み、外国旅行に行ったことのない若者も多く。
どうなるんだ。

ライジングサンってどうなってるんだ。

今は戦後の混乱期と同じだ。

若い人たちで国を作るんだ。

https://twitter.com/CallinShow/status/1538071752802467840(魚拓)

立花先生は、いつも一緒懸命だ。

信念は変わらない。

理不尽との闘い。

立花先生が裁判を起こすことは至極まともなことだ。

だって最後は司法なはずだろ。

でも、裁判は、ひどい判決も多い。

権力に擦り寄ることもある。

そんな全てを曝け出しているのが立花先生だ。

こんな人今までいたか。

https://twitter.com/CallinShow/status/1538072997734793216(魚拓)

立花先生はいつも現場主義。

人々の声を拾い。

それを手練手管で伝えいく。

時に道化を案じるのは、みんなに問題を気づいてほしいからだ。

この3年間、少数政党でこれだけ目立ち続けた政党があるか。

それは全て、NHKを変えたい、人々に良くなってもらいたい。

その一心でしかないと思う。

https://twitter.com/CallinShow/status/1538073614540754944(魚拓)

全てをめくっていくんだよ。

ガーシーさんは、その第一章だ。

許せないことを言うことは大切だと。

ガーシー党は、言わば情報公開政党ってことだろ。

いいじゃないか、澱んだ永田町が少しは澄んでくるんじゃないか。

https://twitter.com/CallinShow/status/1538074524721491968(魚拓)

piped.kavin.rocks/nrJqzmMWUCU(魚拓)

澱んだ街ごと食い荒らすんだ。

https://twitter.com/CallinShow/status/1538582552423780352(魚拓)

弁護士唐澤貴洋への匿名のメッセージを募集中!
#マシュマロを投げ合おう
marshmallow-qa.com/apt/21e00…(魚拓)