Maayong hapon. 2 ka tuig na ang milabay sukad kini gisulat. katapusan nga artikulo mahitungod sa pag-parse sa Habr, ug ang pipila ka mga punto nausab.
Sa diha nga gusto ko nga adunay usa ka kopya sa Habr, nakahukom ko sa pagsulat sa usa ka parser nga makaluwas sa tanan nga mga sulod sa mga tagsulat ngadto sa database. Giunsa kini nahitabo ug unsa nga mga sayup ang akong nasugatan - mahimo nimong basahon ubos sa pagputol.
Ang unang bersyon sa parser. Usa ka thread, daghang problema
Sa pagsugod, nakahukom ko nga maghimo ug script prototype, diin ang artikulo ma-parse dayon sa pag-download ug ibutang sa database. Sa walay paghunahuna sa makaduha, gigamit nako ang sqlite3, tungod kay. dili kaayo labor-intensive: dili kinahanglan nga adunay usa ka lokal nga server, gibuhat-tan-awon-natangtang ug mga butang nga ingon niana.
one_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)
Ang tanan klasiko - gigamit namon ang Matahum nga Sabaw, mga hangyo ug usa ka dali nga prototype andam na. Mao raβ¦
Ang pag-download sa panid anaa sa usa ka thread
Kung gibalda nimo ang pagpatuman sa script, nan ang tibuuk nga database dili moadto bisan diin. Pagkahuman, ang commit gihimo lamang pagkahuman sa tanan nga pag-parse.
Siyempre, mahimo nimong buhaton ang mga pagbag-o sa database pagkahuman sa matag pagsal-ot, apan unya ang oras sa pagpatuman sa script modaghan pag-ayo.
Ang pag-parse sa unang 100 ka artikulo mikuha ug 000 ka oras.
Sunod nakit-an nako ang artikulo sa tiggamit gihiusa, nga akong gibasa ug nakit-an ang pipila ka mga hack sa kinabuhi aron mapadali kini nga proseso:
Ang paggamit sa multithreading makapadali sa pag-download usahay.
Dili nimo makuha ang tibuuk nga bersyon sa habr, apan ang mobile nga bersyon niini.
Pananglitan, kung ang usa ka cointegrated nga artikulo sa desktop nga bersyon adunay gibug-aton nga 378 KB, nan sa mobile nga bersyon kini 126 KB na.
Ikaduha nga bersyon. Daghang mga thread, temporaryo nga pagdili gikan sa Habr
Sa dihang gisuhid nako ang Internet sa hilisgutan sa multithreading sa python, gipili nako ang pinakasimple nga opsyon sa multiprocessing.dummy, akong namatikdan nga ang mga problema nagpakita uban sa multithreading.
SQLite3 dili gusto sa pagtrabaho uban sa labaw pa kay sa usa ka thread.
naayo check_same_thread=False, apan kini nga sayop dili lamang ang usa, sa diha nga naningkamot sa sal-ot ngadto sa database, mga sayop usahay mahitabo nga dili nako masulbad.
Busa, nakahukom ko nga biyaan ang instant insertion sa mga artikulo direkta ngadto sa database ug, sa paghinumdom sa cointegrated nga solusyon, nakahukom ko nga gamiton ang mga file, tungod kay walay mga problema sa multi-threaded nga pagsulat sa usa ka file.
Ang Habr nagsugod sa pagdili sa paggamit sa labaw sa tulo ka mga hilo.
Ilabi na ang madasigon nga mga pagsulay nga makaagi sa Habr mahimong matapos sa usa ka pagdili sa ip sulod sa pipila ka oras. Busa kinahanglan ka nga mogamit lamang sa 3 nga mga hilo, apan kini maayo na, tungod kay ang oras sa pag-uli sa sobra sa 100 nga mga artikulo gikunhoran gikan sa 26 ngadto sa 12 ka segundo.
Angay nga matikdan nga kini nga bersyon medyo dili lig-on, ug ang mga pag-download matag karon ug unya nahulog sa daghang mga artikulo.
Samtang nag-debug sa ikaduhang bersyon, akong nadiskobrehan nga ang Habr, sa kalit lang, adunay API nga gi-access sa mobile nga bersyon sa site. Mas paspas ang pag-load kaysa sa mobile nga bersyon, tungod kay json ra kini, nga dili na kinahanglan nga ma-parse. Sa katapusan, nakahukom ko nga isulat pag-usab ang akong script.
Busa, kay nakit-an kini nga sumpay API, mahimo nimong sugdan ang pag-parse niini.
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)
Naglangkob kini sa mga natad nga adunay kalabotan sa artikulo mismo ug sa tagsulat nga nagsulat niini.
API.png
Wala nako gilabay ang bug-os nga json sa matag artikulo, apan gitipigan lamang ang mga natad nga akong gikinahanglan:
id
is_tutorial
time_published
titulo
sulod
comments_count
lang ang pinulongan diin gisulat ang artikulo. Sa pagkakaron, aduna lang kini en ug ru.
tags_string - tanang tag gikan sa post
pagbasa_ihap
awtor nga
score β rating sa artikulo.
Sa ingon, gamit ang API, gikunhoran nako ang oras sa pagpatuman sa script ngadto sa 8 segundos kada 100 ka url.
Human namo ma-download ang datos nga among gikinahanglan, kinahanglan namo kining iproseso ug isulod sa database. Wala usab akoy problema niini: