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
Вот версия скрипта на python3 (протестирована на python3.5 в Ubuntu 17.10 ).
Как пользоваться:
- Чтобы использовать его, поместите оба кода в файлы. Например, файл кода и файл
script.py
пакета requirement.txt
.
- Беги
pip install -r requirement.txt
.
- Запустите скрипт как пример
python3 script.py pa4080
Он использует несколько библиотек:
Что нужно знать для дальнейшего развития программы (кроме документации необходимого пакета):
- библиотека python: asyncio, json и urllib.parse
- Селекторы CSS ( MDN Web Docs ), а также некоторые HTML. см. также, как использовать css selector в вашем браузере, например, в этой статье
Как это устроено:
- Сначала я создаю простой HTML-загрузчик. Это модифицированная версия из образца, приведенного на aiohttp doc.
- После этого создаем простой парсер командной строки, который принимает имя пользователя и имя выходного файла.
- Создайте парсер для ссылок на темы и основной статьи. Использование pdb и простое манипулирование URL должны сделать эту работу.
- Объедините функцию и поместите основную статью в json, чтобы другая программа могла обработать ее позже.
Некоторая идея, чтобы она могла развиваться дальше
- Создайте другую подкоманду, которая принимает ссылку на модуль даты: это можно сделать, отделив метод для анализа модуля даты в его собственной функции и объединения его с новой подкомандой.
- Кэширование ссылки на модуль даты: создайте кеш-файл json после получения ссылки на потоки. поэтому программе не нужно снова анализировать ссылку. или даже просто кэшировать всю основную статью потока, даже если она не совпадает
Это не самый элегантный ответ, но я думаю, что это лучше, чем использовать bash.
- Он использует Python, что означает, что он может быть использован кроссплатформенным.
- Простая установка, все необходимые пакеты могут быть установлены с помощью pip
- Это может быть развито далее, более читаемая программа, легче это может быть развито.
- Он выполняет ту же работу, что и скрипт bash, всего 13 минут .
sudo apt install python3-bs4 python3-click python3-aiohttp python3-async
но я не могу найти - из какого пакетаasync_timeout
происходит?