script.py
:
#!/usr/bin/python3
from urllib.parse import urljoin
import json
import bs4
import click
import aiohttp
import asyncio
import async_timeout
BASE_URL = 'http://e-bane.net'
async def fetch(session, url):
try:
with async_timeout.timeout(20):
async with session.get(url) as response:
return await response.text()
except asyncio.TimeoutError as e:
print('[{}]{}'.format('timeout error', url))
with async_timeout.timeout(20):
async with session.get(url) as response:
return await response.text()
async def get_result(user):
target_url = 'http://e-bane.net/modules.php?name=Stories_Archive'
res = []
async with aiohttp.ClientSession() as session:
html = await fetch(session, target_url)
html_soup = bs4.BeautifulSoup(html, 'html.parser')
date_module_links = parse_date_module_links(html_soup)
for dm_link in date_module_links:
html = await fetch(session, dm_link)
html_soup = bs4.BeautifulSoup(html, 'html.parser')
thread_links = parse_thread_links(html_soup)
print('[{}]{}'.format(len(thread_links), dm_link))
for t_link in thread_links:
thread_html = await fetch(session, t_link)
t_html_soup = bs4.BeautifulSoup(thread_html, 'html.parser')
if is_article_match(t_html_soup, user):
print('[v]{}'.format(t_link))
# to get main article, uncomment below code
# res.append(get_main_article(t_html_soup))
# code below is used to get thread link
res.append(t_link)
else:
print('[x]{}'.format(t_link))
return res
def parse_date_module_links(page):
a_tags = page.select('ul li a')
hrefs = a_tags = [x.get('href') for x in a_tags]
return [urljoin(BASE_URL, x) for x in hrefs]
def parse_thread_links(page):
a_tags = page.select('table table tr td > a')
hrefs = a_tags = [x.get('href') for x in a_tags]
# filter href with 'file=article'
valid_hrefs = [x for x in hrefs if 'file=article' in x]
return [urljoin(BASE_URL, x) for x in valid_hrefs]
def is_article_match(page, user):
main_article = get_main_article(page)
return main_article.text.startswith(user)
def get_main_article(page):
td_tags = page.select('table table td.row1')
td_tag = td_tags[4]
return td_tag
@click.command()
@click.argument('user')
@click.option('--output-filename', default='out.json', help='Output filename.')
def main(user, output_filename):
loop = asyncio.get_event_loop()
res = loop.run_until_complete(get_result(user))
# if you want to return main article, convert html soup into text
# text_res = [x.text for x in res]
# else just put res on text_res
text_res = res
with open(output_filename, 'w') as f:
json.dump(text_res, f)
if __name__ == '__main__':
main()
requirement.txt
:
aiohttp>=2.3.7
beautifulsoup4>=4.6.0
click>=6.7
Oto wersja skryptu python3 (przetestowana na python3.5 na Ubuntu 17.10 ).
Jak używać:
- Aby go użyć, umieść oba kody w plikach. Przykładowo plik kodu to
script.py
plik pakietu requirement.txt
.
- Uruchom
pip install -r requirement.txt
.
- Uruchom skrypt jako przykład
python3 script.py pa4080
Wykorzystuje kilka bibliotek:
Co należy wiedzieć, aby dalej rozwijać program (inny niż dokument wymaganego pakietu):
- biblioteka python: asyncio, json i urllib.parse
- selektory css ( mdn web docs ), także niektóre pliki HTML. Zobacz także, jak korzystać z selektora css w przeglądarce, na przykład w tym artykule
Jak to działa:
- Najpierw tworzę prosty downloader HTML. Jest to zmodyfikowana wersja z próbki podanej na aiohttp doc.
- Po utworzeniu prostego parsera wiersza poleceń, który akceptuje nazwę użytkownika i wyjściową nazwę pliku.
- Utwórz parser dla linków wątków i głównego artykułu. Używanie pdb i prostych manipulacji adresami URL powinno wystarczyć.
- Połącz funkcję i umieść główny artykuł w json, aby inny program mógł go przetworzyć później.
Trochę pomysłu, dzięki czemu można go dalej rozwijać
- Utwórz kolejną komendę, która akceptuje łącze do modułu daty: można to zrobić, oddzielając metodę parsowania modułu daty do jego własnej funkcji i łącząc go z nową komendą.
- Buforowanie łącza modułu daty: utwórz plik json pamięci podręcznej po uzyskaniu łącza wątków. więc program nie musi ponownie analizować łącza. lub nawet po prostu buforuj cały główny artykuł wątku, nawet jeśli nie pasuje
To nie jest najbardziej elegancka odpowiedź, ale myślę, że jest lepsza niż użycie bash.
- Korzysta z Pythona, co oznacza, że można go używać na różnych platformach.
- Prosta instalacja, wszystkie wymagane pakiety można zainstalować za pomocą pip
- Można go dalej rozwijać, bardziej czytelny program, łatwiej można go rozwijać.
- Działa tak samo jak skrypt bash tylko przez 13 minut .
sudo apt install python3-bs4 python3-click python3-aiohttp python3-async
ale nie mogę znaleźć - z którego pakietuasync_timeout
pochodzi?Aby rozwiązać to zadanie, stworzyłem kolejny prosty skrypt bash, który używa głównie narzędzia CLI
wget
.Skrypt ma trzy funkcje:
Pierwsza funkcja
get_url_map()
zastosowańwget
jak--spider
(co oznacza, że będzie to po prostu sprawdzić, czy strony są tam) i stworzy rekurencyjną-r
URL$MAP_FILE
z poniższych$TARGET_URL
z poziomu głębokości-l2
. (Inny przykład można znaleźć tutaj: Konwertuj stronę internetową na PDF ). W obecnym przypadku$MAP_FILE
zawiera około 20 000 adresów URL.Druga funkcja
filter_url_map()
uprości zawartość$MAP_FILE
. W tym przypadku potrzebujemy tylko wierszy (adresów URL), które zawierają ciąg,article&sid
a są one około 3000. Więcej pomysłów można znaleźć tutaj: Jak usunąć określone słowa z wierszy pliku tekstowego?Trzecia funkcja
get_key_urls()
użyjewget -qO-
(jako poleceniacurl
- przykładów ) do wyprowadzenia zawartości każdego adresu URL z$MAP_FILE
i spróbuje znaleźć dowolny z$KEY_WORDS
nich. Jeśli którykolwiek z elementów$KEY_WORDS
jest oparty na treści określonego adresu URL, ten adres URL zostanie zapisany w pliku$OUT_FILE
.Podczas procesu roboczego dane wyjściowe skryptu wyglądają tak, jak pokazano na następnym obrazie. Zakończenie jest około 63 minut, jeśli są dwa słowa kluczowe, i 42 minuty, gdy wyszukiwane jest tylko jedno słowo kluczowe.
źródło
Odtworzyłem swój skrypt na podstawie odpowiedzi udzielonej przez @karel . Teraz skrypt używa
lynx
zamiastwget
. W rezultacie staje się znacznie szybszy.Obecna wersja wykonuje tę samą pracę przez 15 minut, gdy są dwa wyszukiwane słowa kluczowe i tylko 8 minut, jeśli szukamy tylko jednego słowa kluczowego. Jest to szybsze niż rozwiązanie Python dostarczone przez @dan .
Ponadto
lynx
zapewnia lepszą obsługę znaków spoza alfabetu łacińskiego.źródło