Tráthnóna maith. Tá sé 2 bhliain ó scríobh mé é alt deireanach faoi pharsáil Habr, agus tá roinnt rudaí tar éis athrú.
Nuair a theastaigh uaim cóip de Habr a bheith agam, chinn mé parsálaí a scríobh a shábhálfadh ábhar na n-údar ar fad i mbunachar sonraí. Conas a tharla sé agus cad iad na hearráidí a tháinig mé - is féidir leat léamh faoin gearrtha.
An chéad leagan den pharsálaí. Snáithe amháin, go leor fadhbanna
Ar dtús, chinn mé fréamhshamhail de script a dhéanamh ina ndéanfaí an t-alt a pharsáil agus a chur sa bhunachar sonraí díreach tar éis é a íoslódáil. Gan smaoineamh faoi dhó, d'úsáid mé sqlite3, mar ... ní raibh sé chomh dian ar shaothar: ní gá go mbeadh freastalaí áitiúil agat, cruthaigh, breathnaigh, scrios agus rudaí mar sin.
aon_thread.py
from bs4 import BeautifulSoup
import sqlite3
import requests
from datetime import datetime
def main(min, max):
conn = sqlite3.connect('habr.db')
c = conn.cursor()
c.execute('PRAGMA encoding = "UTF-8"')
c.execute("CREATE TABLE IF NOT EXISTS habr(id INT, author VARCHAR(255), title VARCHAR(255), content TEXT, tags TEXT)")
start_time = datetime.now()
c.execute("begin")
for i in range(min, max):
url = "https://m.habr.com/post/{}".format(i)
try:
r = requests.get(url)
except:
with open("req_errors.txt") as file:
file.write(i)
continue
if(r.status_code != 200):
print("{} - {}".format(i, r.status_code))
continue
html_doc = r.text
soup = BeautifulSoup(html_doc, 'html.parser')
try:
author = soup.find(class_="tm-user-info__username").get_text()
content = soup.find(id="post-content-body")
content = str(content)
title = soup.find(class_="tm-article-title__text").get_text()
tags = soup.find(class_="tm-article__tags").get_text()
tags = tags[5:]
except:
author,title,tags = "Error", "Error {}".format(r.status_code), "Error"
content = "При парсинге этой странице произошла ошибка."
c.execute('INSERT INTO habr VALUES (?, ?, ?, ?, ?)', (i, author, title, content, tags))
print(i)
c.execute("commit")
print(datetime.now() - start_time)
main(1, 490406)
Tá gach rud de réir na clasaiceach - úsáidimid Anraith Álainn, iarratais agus tá an fhréamhshamhail tapa réidh. Sin díreach ...
Íoslódáil an leathanach i snáithe amháin
Má chuireann tú isteach ar fhorghníomhú na scripte, ní rachaidh an bunachar sonraí ar fad in áit ar bith. Tar éis an tsaoil, ní dhéantar an gealltanas a fhorghníomhú ach amháin tar éis an pharsáil ar fad.
Ar ndóigh, is féidir leat athruithe a dhéanamh ar an mbunachar sonraí tar éis gach cuir isteach, ach ansin méadóidh an t-am forghníomhaithe script go suntasach.
Thóg sé 100 n-uaire an chloig dom an chéad 000 alt a pharsáil.
Ansin faighim alt an úsáideora comhtháite, a léigh mé agus a fuair mé roinnt hacks saoil chun an próiseas seo a bhrostú:
Trí úsáid a bhaint as multithreading luasann an íoslódáil go suntasach.
Ní féidir leat an leagan iomlán de Habr a fháil, ach a leagan soghluaiste.
Mar shampla, má tá meáchan 378 KB ag alt comhtháite sa leagan deisce, ansin sa leagan soghluaiste tá sé 126 KB cheana féin.
Dara leagan. Snáitheanna go leor, toirmeasc sealadach ó Habr
Nuair a rinne mé sciúradh ar an Idirlíon ar an ábhar multithreading i python agus roghnaigh an rogha is simplí le multiprocessing.dummy, thug mé faoi deara go raibh an chuma fadhbanna chomh maith le multithreading.
Níl SQLite3 ag iarraidh oibriú le níos mó ná snáithe amháin.
Seasta check_same_thread=False, ach ní hé seo an earráid amháin; agus mé ag iarraidh a chur isteach sa bhunachar sonraí, uaireanta tagann earráidí chun cinn nach raibh mé in ann a réiteach.
Dá bhrí sin, cinneann mé cur isteach toirt na n-alt go díreach isteach sa bhunachar sonraí a thréigean agus, ag cuimhneamh ar an réiteach comhtháthaithe, cinnim comhaid a úsáid, ós rud é nach bhfuil aon fhadhbanna ann le scríobh il-snáithithe chuig comhad.
Tosaíonn Habr toirmeasc as níos mó ná trí snáitheanna a úsáid.
D’fhéadfadh toirmeasc IP cúpla uair an chloig a bheith mar thoradh ar iarrachtaí díograiseacha go háirithe Habr a bhaint amach. Mar sin ní mór duit ach 3 snáithe a úsáid, ach tá sé seo go maith cheana féin, ós rud é go laghdaítear an t-am chun 100 alt a shórtáil ó 26 go 12 soicind.
Is fiú a thabhairt faoi deara go bhfuil an leagan seo go leor éagobhsaí, agus go mainneoidh an íoslódáil go tréimhsiúil ar líon mór alt.
async_v1.py
from bs4 import BeautifulSoup
import requests
import os, sys
import json
from multiprocessing.dummy import Pool as ThreadPool
from datetime import datetime
import logging
def worker(i):
currentFile = "files\{}.json".format(i)
if os.path.isfile(currentFile):
logging.info("{} - File exists".format(i))
return 1
url = "https://m.habr.com/post/{}".format(i)
try: r = requests.get(url)
except:
with open("req_errors.txt") as file:
file.write(i)
return 2
# Запись заблокированных запросов на сервер
if (r.status_code == 503):
with open("Error503.txt", "a") as write_file:
write_file.write(str(i) + "n")
logging.warning('{} / 503 Error'.format(i))
# Если поста не существует или он был скрыт
if (r.status_code != 200):
logging.info("{} / {} Code".format(i, r.status_code))
return r.status_code
html_doc = r.text
soup = BeautifulSoup(html_doc, 'html5lib')
try:
author = soup.find(class_="tm-user-info__username").get_text()
timestamp = soup.find(class_='tm-user-meta__date')
timestamp = timestamp['title']
content = soup.find(id="post-content-body")
content = str(content)
title = soup.find(class_="tm-article-title__text").get_text()
tags = soup.find(class_="tm-article__tags").get_text()
tags = tags[5:]
# Метка, что пост является переводом или туториалом.
tm_tag = soup.find(class_="tm-tags tm-tags_post").get_text()
rating = soup.find(class_="tm-votes-score").get_text()
except:
author = title = tags = timestamp = tm_tag = rating = "Error"
content = "При парсинге этой странице произошла ошибка."
logging.warning("Error parsing - {}".format(i))
with open("Errors.txt", "a") as write_file:
write_file.write(str(i) + "n")
# Записываем статью в json
try:
article = [i, timestamp, author, title, content, tm_tag, rating, tags]
with open(currentFile, "w") as write_file:
json.dump(article, write_file)
except:
print(i)
raise
if __name__ == '__main__':
if len(sys.argv) < 3:
print("Необходимы параметры min и max. Использование: async_v1.py 1 100")
sys.exit(1)
min = int(sys.argv[1])
max = int(sys.argv[2])
# Если потоков >3
# то хабр банит ipшник на время
pool = ThreadPool(3)
# Отсчет времени, запуск потоков
start_time = datetime.now()
results = pool.map(worker, range(min, max))
# После закрытия всех потоков печатаем время
pool.close()
pool.join()
print(datetime.now() - start_time)
Tríú leagan. Deiridh
Agus an dara leagan á dhífhabhtú agam, fuair mé amach go bhfuil API ag Habr go tobann a bhfuil rochtain ag leagan soghluaiste an tsuímh air. Luchtaíonn sé níos tapúla ná an leagan soghluaiste, ós rud é nach bhfuil ann ach json, nach gá fiú a pharsáil. Sa deireadh, chinn mé mo script a athscríobh arís.
Mar sin, tar éis a fháil amach an nasc seo API, is féidir leat é a pharsáil.
async_v2.py
import requests
import os, sys
import json
from multiprocessing.dummy import Pool as ThreadPool
from datetime import datetime
import logging
def worker(i):
currentFile = "files\{}.json".format(i)
if os.path.isfile(currentFile):
logging.info("{} - File exists".format(i))
return 1
url = "https://m.habr.com/kek/v1/articles/{}/?fl=ru%2Cen&hl=ru".format(i)
try:
r = requests.get(url)
if r.status_code == 503:
logging.critical("503 Error")
return 503
except:
with open("req_errors.txt") as file:
file.write(i)
return 2
data = json.loads(r.text)
if data['success']:
article = data['data']['article']
id = article['id']
is_tutorial = article['is_tutorial']
time_published = article['time_published']
comments_count = article['comments_count']
lang = article['lang']
tags_string = article['tags_string']
title = article['title']
content = article['text_html']
reading_count = article['reading_count']
author = article['author']['login']
score = article['voting']['score']
data = (id, is_tutorial, time_published, title, content, comments_count, lang, tags_string, reading_count, author, score)
with open(currentFile, "w") as write_file:
json.dump(data, write_file)
if __name__ == '__main__':
if len(sys.argv) < 3:
print("Необходимы параметры min и max. Использование: asyc.py 1 100")
sys.exit(1)
min = int(sys.argv[1])
max = int(sys.argv[2])
# Если потоков >3
# то хабр банит ipшник на время
pool = ThreadPool(3)
# Отсчет времени, запуск потоков
start_time = datetime.now()
results = pool.map(worker, range(min, max))
# После закрытия всех потоков печатаем время
pool.close()
pool.join()
print(datetime.now() - start_time)
Tá réimsí ann a bhaineann leis an alt féin agus leis an údar a scríobh é.
API.png
Níor dhumpáil mé json iomlán gach ailt, ach níor shábháil mé ach na réimsí a bhí de dhíth orm:
id
is_teagasc
am_foilsithe
teideal
ábhar
tuairimí_comhaireamh
lang an teanga ina bhfuil an t-alt scríofa. Go dtí seo níl ann ach en agus ru.
tags_string — gach clib ón bpost
léamh_áireamh
údar
scór — rátáil alt.
Mar sin, ag baint úsáide as an API, laghdaigh mé an t-am forghníomhaithe script go 8 soicind in aghaidh an 100 url.
Tar éis dúinn na sonraí a theastaíonn uainn a íoslódáil, ní mór dúinn iad a phróiseáil agus a chur isteach sa bhunachar sonraí. Ní raibh aon fadhbanna leis seo ach an oiread:
parsálaí.py
import json
import sqlite3
import logging
from datetime import datetime
def parser(min, max):
conn = sqlite3.connect('habr.db')
c = conn.cursor()
c.execute('PRAGMA encoding = "UTF-8"')
c.execute('PRAGMA synchronous = 0') # Отключаем подтверждение записи, так скорость увеличивается в разы.
c.execute("CREATE TABLE IF NOT EXISTS articles(id INTEGER, time_published TEXT, author TEXT, title TEXT, content TEXT,
lang TEXT, comments_count INTEGER, reading_count INTEGER, score INTEGER, is_tutorial INTEGER, tags_string TEXT)")
try:
for i in range(min, max):
try:
filename = "files\{}.json".format(i)
f = open(filename)
data = json.load(f)
(id, is_tutorial, time_published, title, content, comments_count, lang,
tags_string, reading_count, author, score) = data
# Ради лучшей читаемости базы можно пренебречь читаемостью кода. Или нет?
# Если вам так кажется, можно просто заменить кортеж аргументом data. Решать вам.
c.execute('INSERT INTO articles VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', (id, time_published, author,
title, content, lang,
comments_count, reading_count,
score, is_tutorial,
tags_string))
f.close()
except IOError:
logging.info('FileNotExists')
continue
finally:
conn.commit()
start_time = datetime.now()
parser(490000, 490918)
print(datetime.now() - start_time)
Staitisticí
Bhuel, go traidisiúnta, ar deireadh, is féidir leat roinnt staitisticí a bhaint as na sonraí:
As na 490 a rabhthas ag súil leo, níor íoslódáladh ach 406 alt. Tharlaíonn sé go raibh níos mó ná leath (228) de na hailt ar Habré folaithe nó scriosta.
Tá meáchan 2.95 GB sa bhunachar sonraí iomlán, ina bhfuil beagnach leath mhilliún alt. I bhfoirm chomhbhrúite - 495 MB.
San iomlán, tá 37804 údar ar Habré. Cuir i gcuimhne duit nach staitisticí iad seo ach ó phoist bheo.
An t-údar is táirgiúla ar Habré - alizar — 8774 alt.