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版本(已在Ubuntu 17.10的 python3.5上进行了测试)。
如何使用:
- 要使用它,请将两个代码都放在文件中。例如,代码文件为
script.py
,包文件为requirement.txt
。
- 运行
pip install -r requirement.txt
。
- 运行脚本作为示例
python3 script.py pa4080
它使用了几个库:
进一步开发程序需要了解的事项(所需软件包的文档除外):
- python库:asyncio,json和urllib.parse
- css选择器(mdn web docs),还有一些html。另请参阅如何在浏览器(如本文)上使用css选择器
这个怎么运作:
- 首先,我创建一个简单的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
来自?