ሁሉም Habr በአንድ የውሂብ ጎታ

እንደምን አረፈድክ. ከተፃፈ 2 አመት ሆኖታል። የመጨረሻው ጽሑፍ ሀብርን ስለመተንተን እና አንዳንድ ነጥቦች ተለውጠዋል።

የሀብር ቅጂ እንዲኖረኝ በፈለግኩ ጊዜ ሁሉንም የጸሐፊዎችን ይዘት ወደ ዳታቤዝ የሚያድን ትንታኔ ለመጻፍ ወሰንኩ። እንዴት እንደተከሰተ እና ምን ስህተቶች እንዳጋጠሙኝ - በቆራጩ ስር ማንበብ ይችላሉ.

TLDR- የውሂብ ጎታ አገናኝ

የመተንተን የመጀመሪያው ስሪት. አንድ ክር ፣ ብዙ ችግሮች

ለመጀመር ፣ ጽሑፉ ሲወርድ ወዲያውኑ የሚተነተን እና በመረጃ ቋቱ ውስጥ የሚቀመጥበትን የስክሪፕት ፕሮቶታይፕ ለማድረግ ወሰንኩ። ሁለት ጊዜ ሳላስብ, እኔ sqlite3 ተጠቀምኩኝ, ምክንያቱም. ብዙም ጉልበት የሚጠይቅ ነበር፡ የአገር ውስጥ አገልጋይ፣ የተፈጠረ የተሰረዘ እና የመሳሰሉት ነገሮች መኖር አያስፈልግም።

አንድ_ክር.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 ኪ.ባ ይመዝናል ፣ ከዚያ በሞባይል ሥሪት ውስጥ ቀድሞውኑ 126 ኪባ ነው።

ሁለተኛ ስሪት. ብዙ ክሮች፣ ጊዜያዊ እገዳ ከሀብር

በፓይቶን ውስጥ ባለ ብዙ ስክሪፕት ርዕስ ላይ በይነመረብን ስቃኝ ፣ በጣም ቀላሉን አማራጭ ከ 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)

ሦስተኛው ስሪት. የመጨረሻ

ሁለተኛውን እትም እያረምኩ ሳለ ሃብር፣ በድንገት፣ የጣቢያው ሞባይል ስሪት የሚደርሰው ኤፒአይ እንዳለው ተረዳሁ። Json ብቻ ስለሆነ ከሞባይል ስሪቱ በበለጠ ፍጥነት ይጫናል፣ ይህም መተንተን እንኳን አያስፈልገውም። በመጨረሻ ፣ ስክሪፕቴን እንደገና ለመፃፍ ወሰንኩ ።

ስለዚህ, በማግኘቱ ይህ አገናኝ ኤፒአይ፣ መተንተን መጀመር ትችላለህ።

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

ሁሉም Habr በአንድ የውሂብ ጎታ

የእያንዳንዱን መጣጥፍ ሙሉ json አልጣልኩትም፣ ነገር ግን የሚያስፈልጓቸውን መስኮች ብቻ አስቀምጫለሁ፡

  • id
  • ትምህርት_ነው
  • ጊዜ_የታተመ
  • አርእስት
  • ይዘት
  • አስተያየቶች_ቁጥር
  • lang ጽሑፉ የተጻፈበት ቋንቋ ነው። እስካሁን ድረስ, en እና ru ብቻ አለው.
  • tags_string - ሁሉም መለያዎች ከልጥፉ
  • የንባብ_ቁጥር
  • ደራሲ
  • ውጤት - ጽሑፍ ደረጃ.

ስለዚህ ኤፒአይን በመጠቀም የስክሪፕት አፈጻጸም ጊዜን በ8 ዩአርኤል ወደ 100 ሰከንድ ቀንሼዋለሁ።

የሚያስፈልገንን መረጃ ካወረድን በኋላ ወደ ዳታቤዝ ውስጥ ማስገባት አለብን. እኔም በዚህ ላይ ምንም ችግር አላጋጠመኝም:

ተንታኝ.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 ጂቢ ይመዝናል። በተጨመቀ ቅጽ - 495 ሜባ.
  • በአጠቃላይ 37804 ሰዎች የሀበሬ ደራሲ ናቸው። እነዚህ ስታቲስቲክስ ከቀጥታ ልጥፎች ብቻ እንደሆኑ አስታውሳችኋለሁ።
  • በሀበሬ ላይ በጣም ውጤታማ ደራሲ - አሊዛር - 8774 ጽሑፎች.
  • ከፍተኛ ደረጃ የተሰጠው ጽሑፍ - 1448 ፕላስ
  • በጣም የተነበበ ጽሑፍ - 1660841 እይታዎች
  • ብዙ ውይይት የተደረገበት አንቀጽ - 2444 አስተያየቶች

መልካም, በከፍታዎች መልክምርጥ 15 ደራሲዎችሁሉም Habr በአንድ የውሂብ ጎታ
ከፍተኛ 15 ደረጃ በመስጠትሁሉም Habr በአንድ የውሂብ ጎታ
ከፍተኛ 15 የተነበበሁሉም Habr በአንድ የውሂብ ጎታ
ከፍተኛ 15 ተወያይተዋል።ሁሉም Habr በአንድ የውሂብ ጎታ

ምንጭ: hab.com

አስተያየት ያክሉ