ټول هابر په یوه ډیټابیس کې

بعد له غرمه مو پخیر. له لیکلو یې دوه کاله تېر شول. وروستۍ مقاله د حبر د تحلیل په اړه، او ځینې ټکي بدل شوي.

کله چې ما غوښتل د حبر یوه کاپي ولرم، ما پریکړه وکړه چې یو پارسر ولیکم چې د لیکوالانو ټول مینځپانګې ډیټابیس ته خوندي کړي. دا څنګه پیښ شوي او زه له کومو غلطیو سره مخ شوم - تاسو کولی شئ د کټ لاندې ولولئ.

TLDR- د ډیټابیس لینک

د پارسر لومړۍ نسخه. یو تار، ډیری ستونزې

د پیل کولو لپاره ، ما پریکړه وکړه چې د سکریپټ پروټوټایپ جوړ کړم په کوم کې چې مقاله به د ډاونلوډ کولو سره سمدلاسه په ډیټابیس کې کیښودل شي. د دوه ځله فکر کولو پرته، ما sqlite3 کارولی، ځکه. دا لږ کار لرونکی و: اړتیا نشته چې محلي سرور ولرئ، جوړ شوي لیدل شوي - حذف شوي او ورته توکي.

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)

هرڅه کلاسیک دي - موږ ښکلی سوپ کاروو، غوښتنې او یو چټک پروټوټایپ چمتو دی. دا یوازې…

  • د پاڼې ډاونلوډ په یوه موضوع کې دی

  • که تاسو د سکریپټ اجرا کولو کې مداخله وکړئ، نو ټول ډیټابیس به هیڅ ځای ته لاړ نشي. په هرصورت ، ژمنې یوازې د ټولو تحلیل کولو وروسته ترسره کیږي.
    البته، تاسو کولی شئ د هر داخلولو وروسته ډیټابیس کې بدلونونه ترسره کړئ، مګر بیا د سکریپټ اجرا کولو وخت به د پام وړ زیاتوالی ومومي.

  • د لومړیو 100 مقالو تحلیل کول ما 000 ساعته وخت واخیست.

بل زه د کاروونکي مقاله ومومم یوځای شوی، کوم چې ما لوستل او د دې پروسې ګړندي کولو لپاره د ژوند یو څو هیکونه وموندل:

  • د ملټي تریډینګ کارول په وختونو کې د ډاونلوډ سرعت زیاتوي.
  • تاسو د هابر بشپړ نسخه نه ترلاسه کولی شئ ، مګر د دې ګرځنده نسخه.
    د مثال په توګه، که په ډیسټاپ نسخه کې یو همغږي مقاله 378 KB وزن ولري، نو په ګرځنده نسخه کې دا دمخه 126 KB دی.

دویمه نسخه. ډیری تارونه، د حبر څخه لنډمهاله بندیز

کله چې ما په python کې د ملټي تھریډینګ موضوع باندې انټرنیټ وټاکه ، ما د multiprocessing.dummy سره خورا ساده اختیار غوره کړ ، ما ولیدل چې د ملټي ریډینګ سره ستونزې څرګندې شوې.

SQLite3 نه غواړي له یو څخه ډیر تار سره کار وکړي.
ثابت check_same_thread=False، مګر دا تېروتنه یوازینۍ نه ده ، کله چې ډیټابیس ته د ننوتلو هڅه کیږي ، ځینې وختونه غلطۍ رامینځته کیږي چې زه نشم حل کولی.

له همدې امله ، زه پریکړه کوم چې مستقیم ډیټابیس ته د مقالو سمدستي داخلول پریږدم او د یوځای شوي حل په یادولو سره ، زه پریکړه کوم چې فایلونه وکاروم ، ځکه چې فایل ته د څو اړخیزو لیکلو سره کومه ستونزه شتون نلري.

حبر له دریو څخه زیاتو تارونو کارولو بندیز پیل کوي.
په ځانګړي توګه هابر ته د رسیدو په زړه پورې هڅې کولی شي د څو ساعتونو لپاره د آی پی بندیز سره پای ته ورسیږي. نو تاسو باید یوازې 3 تارونه وکاروئ ، مګر دا دمخه ښه دی ، ځکه چې له 100 څخه زیاتو مقالو تکرار کولو وخت له 26 څخه 12 ثانیو ته راټیټ شوی.

دا د یادولو وړ ده چې دا نسخه خورا بې ثباته ده، او ډاونلوډونه په منظمه توګه په ډیری مقالو کې راځي.

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)

دریمه نسخه. وروستی

پداسې حال کې چې د دویمې نسخې ډیبګ کول، ما وموندله چې Habr، په ناڅاپه توګه، یو API لري چې د سایټ ګرځنده نسخه لاسرسی لري. دا د ګرځنده نسخې په پرتله ګړندی باریږي ، ځکه چې دا یوازې json دی ، کوم چې حتی تحلیل کولو ته اړتیا نلري. په پای کې، ما پریکړه وکړه چې خپل سکریپټ بیا بیا ولیکم.

نو، موندل دا لینک API، تاسو کولی شئ دا پارس کول پیل کړئ.

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)

دا پخپله د مقالې او لیکوال پورې اړوند ساحې لري چې دا یې لیکلي.

API.png

ټول هابر په یوه ډیټابیس کې

ما د هرې مقالې بشپړ json ډمپ نه کړ، مګر یوازې هغه ساحې خوندي کړې چې زه ورته اړتیا لرم:

  • id
  • is_tutorial
  • وخت_کابل شوی
  • سرليک
  • محتوا
  • نظرونه_شمیر
  • lang هغه ژبه ده چې مقاله پکې لیکل کیږي. تر اوسه پورې، دا یوازې en او ru لري.
  • tags_string - د پوسټ څخه ټول ټګونه
  • لوستل_ شمېرنه
  • لیکوال
  • نمرې - د مقالې درجه بندي.

پدې توګه ، د API په کارولو سره ، ما د سکریپټ اجرا کولو وخت په هر 8 یو آر ایل کې 100 ثانیو ته راکم کړ.

وروسته لدې چې موږ هغه ډیټا ډاونلوډ کړو چې موږ ورته اړتیا لرو ، موږ اړتیا لرو چې دا پروسس کړو او ډیټابیس ته یې دننه کړو. ما په دې کې هم کومه ستونزه نه درلوده:

parser.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)

Статистика

ښه، په دودیز ډول، په پای کې، تاسو کولی شئ د معلوماتو څخه ځینې احصایې استخراج کړئ:

  • د متوقع 490 ډاونلوډونو څخه، یوازې 406 مقالې ډاونلوډ شوي. دا معلومه شوه چې د هابري په اړه له نیمایي څخه ډیر (228) مقالې پټ یا حذف شوي.
  • ټول ډیټابیس چې نږدې نیم ملیون مقالې لري، 2.95 GB وزن لري. په کمپریس شوي شکل کې - 495 MB.
  • په مجموع کې، 37804 خلک د Habré لیکوالان دي. زه تاسو ته یادونه کوم چې دا احصایې یوازې د ژوندي پوسټونو څخه دي.
  • په هابري کې ترټولو ګټور لیکوال - علیزر - 8774 مقالې.
  • غوره درجه شوې مقاله - 1448 پلس
  • ډیری لوستل شوي مقاله — 1660841 لیدنې
  • ډیری بحث شوی مقاله - 2444 تبصرې

ښه، د سرونو په بڼه15 غوره لیکوالانټول هابر په یوه ډیټابیس کې
د درجه بندي له مخې غوره 15ټول هابر په یوه ډیټابیس کې
غوره 15 لوستلټول هابر په یوه ډیټابیس کې
پورته 15 بحث شویټول هابر په یوه ډیټابیس کې

سرچینه: www.habr.com

Add a comment