Π’Π΅ΡΡŒ Π₯Π°Π±Ρ€ Π² ΠΎΠ΄Π½ΠΎΠΉ Π±Π°Π·Π΅

Π”ΠΎΠ±Ρ€Ρ‹ΠΉ дСнь. ΠŸΡ€ΠΎΡˆΠ»ΠΎ ΡƒΠΆΠ΅ 2 Π³ΠΎΠ΄Π° с ΠΌΠΎΠΌΠ΅Π½Ρ‚Π° написания послСднСй ΡΡ‚Π°Ρ‚ΡŒΠΈ ΠΏΡ€ΠΎ парсинг Π₯Π°Π±Ρ€Π°, ΠΈ Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΌΠΎΠΌΠ΅Π½Ρ‚Ρ‹ измСнились.

Когда я Π·Π°Ρ…ΠΎΡ‚Π΅Π» ΠΈΠΌΠ΅Ρ‚ΡŒ Ρƒ сСбя копию Ρ…Π°Π±Ρ€Π°, я Ρ€Π΅ΡˆΠΈΠ» Π½Π°ΠΏΠΈΡΠ°Ρ‚ΡŒ парсСр, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π±Ρ‹ сохранил вСсь ΠΊΠΎΠ½Ρ‚Π΅Π½Ρ‚ Π°Π²Ρ‚ΠΎΡ€ΠΎΠ² Π² Π±Π°Π·Ρƒ Π΄Π°Π½Π½Ρ‹Ρ…. Как это Π²Ρ‹ΡˆΠ»ΠΎ ΠΈ с ΠΊΠ°ΠΊΠΈΠΌΠΈ ошибками я встрСтился β€” ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΠΏΡ€ΠΎΡ‡ΠΈΡ‚Π°Ρ‚ΡŒ ΠΏΠΎΠ΄ ΠΊΠ°Ρ‚ΠΎΠΌ.

TL;DR β€” ссылка Π½Π° Π±Π°Π·Ρƒ Π΄Π°Π½Π½Ρ‹Ρ…

ΠŸΠ΅Ρ€Π²Π°Ρ вСрсия парсСра. Один ΠΏΠΎΡ‚ΠΎΠΊ, ΠΌΠ½ΠΎΠ³ΠΎ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌ

Для Π½Π°Ρ‡Π°Π»Π°, я Ρ€Π΅ΡˆΠΈΠ» ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ ΠΏΡ€ΠΎΡ‚ΠΎΡ‚ΠΈΠΏ скрипта, Π² ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ Π±Ρ‹ сразу ΠΏΡ€ΠΈ скачивании ΡΡ‚Π°Ρ‚ΡŒΡ ΠΏΠ°Ρ€ΡΠΈΠ»Π°ΡΡŒ ΠΈ ΠΏΠΎΠΌΠ΅Ρ‰Π°Π»Π°ΡΡŒ Π² Π±Π°Π·Ρƒ Π΄Π°Π½Π½Ρ‹Ρ…. НСдолго Π΄ΡƒΠΌΠ°Π², использовал 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)

Всё ΠΏΠΎ классикС β€” ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ Beautiful Soup, requests ΠΈ быстрый ΠΏΡ€ΠΎΡ‚ΠΎΡ‚ΠΈΠΏ Π³ΠΎΡ‚ΠΎΠ². Π’ΠΎΡ‚ Ρ‚ΠΎΠ»ΡŒΠΊΠΎβ€¦

  • Π‘ΠΊΠ°Ρ‡ΠΈΠ²Π°Π½ΠΈΠ΅ страницы ΠΈΠ΄Π΅Ρ‚ Π² ΠΎΠ΄ΠΈΠ½ ΠΏΠΎΡ‚ΠΎΠΊ

  • Если ΠΎΠ±ΠΎΡ€Π²Π°Ρ‚ΡŒ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ скрипта, Ρ‚ΠΎ вся Π±Π°Π·Π° ΡƒΠΉΠ΄Π΅Ρ‚ Π² Π½ΠΈΠΊΡƒΠ΄Π°. Π’Π΅Π΄ΡŒ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ ΠΊΠΎΠΌΠΌΠΈΡ‚Π° Ρ‚ΠΎΠ»ΡŒΠΊΠΎ послС всСго парсинга.
    ΠšΠΎΠ½Π΅Ρ‡Π½ΠΎ, ΠΌΠΎΠΆΠ½ΠΎ Π·Π°ΠΊΡ€Π΅ΠΏΠ»ΡΡ‚ΡŒ измСнСния Π² Π±Π°Π·Π΅ послС ΠΊΠ°ΠΆΠ΄ΠΎΠΉ вставки, Π½ΠΎ Ρ‚ΠΎΠ³Π΄Π° ΠΈ врСмя выполнСния скрипта увСличится Π² Ρ€Π°Π·Ρ‹.

  • ΠŸΠ°Ρ€ΡΠΈΠ½Π³ ΠΏΠ΅Ρ€Π²Ρ‹Ρ… 100 000 статСй Ρƒ мСня занял 8 часов.

Π”Π°Π»ΡŒΡˆΠ΅ я Π½Π°Ρ…ΠΎΠΆΡƒ ΡΡ‚Π°Ρ‚ΡŒΡŽ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ cointegrated, ΠΊΠΎΡ‚ΠΎΡ€ΡƒΡŽ я ΠΏΡ€ΠΎΡ‡ΠΈΡ‚Π°Π» ΠΈ нашСл нСсколько Π»Π°ΠΉΡ„Ρ…Π°ΠΊΠΎΠ², ΠΏΠΎΠ·Π²ΠΎΠ»ΡΡŽΡ‰ΠΈΡ… ΡƒΡΠΊΠΎΡ€ΠΈΡ‚ΡŒ сСй процСсс:

  • ИспользованиС многопоточности ускоряСт скачиваниС Π² Ρ€Π°Π·Ρ‹.
  • МоТно ΠΏΠΎΠ»ΡƒΡ‡Π°Ρ‚ΡŒ Π½Π΅ ΠΏΠΎΠ»Π½ΡƒΡŽ Π²Π΅Ρ€ΡΠΈΡŽ Ρ…Π°Π±Ρ€Π°, Π° Π΅Π³ΠΎ ΠΌΠΎΠ±ΠΈΠ»ΡŒΠ½ΡƒΡŽ Π²Π΅Ρ€ΡΠΈΡŽ.
    НапримСр, Ссли ΡΡ‚Π°Ρ‚ΡŒΡ cointegrated Π² дСсктопной вСрсии вСсит 378 Кб, Ρ‚ΠΎ Π² мобильной ΡƒΠΆΠ΅ 126 Кб.

Вторая вСрсия. Много ΠΏΠΎΡ‚ΠΎΠΊΠΎΠ², Π²Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹ΠΉ Π±Π°Π½ ΠΎΡ‚ Π₯Π°Π±Ρ€Π°

Когда я ΠΏΡ€ΠΎΡˆΠ΅Ρ€ΡΡ‚ΠΈΠ» ΠΈΠ½Ρ‚Π΅Ρ€Π½Π΅Ρ‚ Π½Π° Ρ‚Π΅ΠΌΡƒ многопоточности Π² python, Π²Ρ‹Π±Ρ€Π°Π» Π½Π°ΠΈΠ±ΠΎΠ»Π΅Π΅ простой Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ с multiprocessing.dummy, Ρ‚ΠΎ я Π·Π°ΠΌΠ΅Ρ‚ΠΈΠ», Ρ‡Ρ‚ΠΎ вмСстС с ΠΌΠ½ΠΎΠ³ΠΎΠΏΠΎΡ‚ΠΎΡ‡Π½ΠΎΡΡ‚ΡŒΡŽ появились ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹.

SQLite3 Π½Π΅ Ρ…ΠΎΡ‡Π΅Ρ‚ Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ с Π±ΠΎΠ»Π΅Π΅ Ρ‡Π΅ΠΌ ΠΎΠ΄Π½ΠΈΠΌ ΠΏΠΎΡ‚ΠΎΠΊΠΎΠΌ.
Ѐиксится check_same_thread=False, Π½ΠΎ эта ошибка Π½Π΅ СдинствСнная, ΠΏΡ€ΠΈ ΠΏΠΎΠΏΡ‹Ρ‚ΠΊΠ΅ вставки Π² Π±Π°Π·Ρƒ ΠΈΠ½ΠΎΠ³Π΄Π° Π²ΠΎΠ·Π½ΠΈΠΊΠ°ΡŽΡ‚ ошибки, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ я Ρ‚Π°ΠΊ ΠΈ Π½Π΅ смог Ρ€Π΅ΡˆΠΈΡ‚ΡŒ.

ΠŸΠΎΡΡ‚ΠΎΠΌΡƒ я Ρ€Π΅ΡˆΠ°ΡŽ ΠΎΡ‚ΠΊΠ°Π·Π°Ρ‚ΡŒΡΡ ΠΎΡ‚ ΠΌΠ³Π½ΠΎΠ²Π΅Π½Π½ΠΎΠΉ вставки статСй сразу Π² Π±Π°Π·Ρƒ ΠΈ, вспоминая Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅ cointegrated, Ρ€Π΅ΡˆΠ°ΡŽ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Ρ„Π°ΠΉΠ»Ρ‹, Ρ‚.ΠΊ Π½ΠΈΠΊΠ°ΠΊΠΈΡ… ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌ с ΠΌΠ½ΠΎΠ³ΠΎΠΏΠΎΡ‚ΠΎΡ‡Π½ΠΎΠΉ записью Π² Ρ„Π°ΠΉΠ» Π½Π΅Ρ‚.

Π₯Π°Π±Ρ€ Π½Π°Ρ‡ΠΈΠ½Π°Π΅Ρ‚ Π±Π°Π½ΠΈΡ‚ΡŒ Π·Π° использованиС Π±ΠΎΠ»Π΅Π΅ Ρ‡Π΅ΠΌ Ρ‚Ρ€Π΅Ρ… ΠΏΠΎΡ‚ΠΎΠΊΠΎΠ².
Особо Ρ€ΡŒΡΠ½Ρ‹Π΅ ΠΏΠΎΠΏΡ‹Ρ‚ΠΊΠΈ Π΄ΠΎΡΡ‚ΡƒΡ‡Π°Ρ‚ΡŒΡΡ Π΄ΠΎ Π₯Π°Π±Ρ€Π° ΠΌΠΎΠ³ΡƒΡ‚ закончится Π±Π°Π½ΠΎΠΌ ip Π½Π° ΠΏΠ°Ρ€Ρƒ часов. Π’Π°ΠΊ Ρ‡Ρ‚ΠΎ приходится ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ лишь 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)

Π’Ρ€Π΅Ρ‚ΡŒΡ вСрсия. Ѐинальная

ΠžΡ‚Π»Π°ΠΆΠΈΠ²Π°Ρ Π²Ρ‚ΠΎΡ€ΡƒΡŽ Π²Π΅Ρ€ΡΠΈΡŽ, я ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠΈΠ», Ρ‡Ρ‚ΠΎ Ρƒ Π₯Π°Π±Ρ€Π°, Π²Π½Π΅Π·Π°ΠΏΠ½ΠΎ, Π΅ΡΡ‚ΡŒ 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
  • time_published
  • title
  • content
  • comments_count
  • lang β€” язык, Π½Π° ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ написана ΡΡ‚Π°Ρ‚ΡŒΡ. Пока Ρ‡Ρ‚ΠΎ Π² Π½Π΅ΠΉ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ en ΠΈ ru.
  • tags_string β€” всС Ρ‚Π΅Π³ΠΈ ΠΈΠ· поста
  • reading_count
  • author
  • score β€” Ρ€Π΅ΠΉΡ‚ΠΈΠ½Π³ ΡΡ‚Π°Ρ‚ΡŒΠΈ.

Π’Π°ΠΊΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ API, я ΡƒΠΌΠ΅Π½ΡŒΡˆΠΈΠ» врСмя выполнСния скрипта Π΄ΠΎ 8 сСкунд Π½Π° 100 url.

ПослС Ρ‚ΠΎΠ³ΠΎ, ΠΊΠ°ΠΊ ΠΌΡ‹ скачали Π½ΡƒΠΆΠ½Ρ‹Π΅ Π½Π°ΠΌ Π΄Π°Π½Π½Ρ‹Π΅, Π½ΡƒΠΆΠ½ΠΎ ΠΈΡ… ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ ΠΈ внСсти Π² Π±Π°Π·Ρƒ Π΄Π°Π½Π½Ρ‹Ρ…. Π‘ этим Ρ‚ΠΎΠΆΠ΅ Π½Π΅ Π²ΠΎΠ·Π½ΠΈΠΊΠ»ΠΎ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌ:

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 512 статСй. ΠŸΠΎΠ»ΡƒΡ‡Π°Π΅Ρ‚ΡΡ, Ρ‡Ρ‚ΠΎ Π±ΠΎΠ»Π΅Π΅ ΠΏΠΎΠ»ΠΎΠ²ΠΈΠ½Ρ‹(261894) статСй Π½Π° Ρ…Π°Π±Ρ€Π΅ Π±Ρ‹Π»ΠΎ скрыто ΠΈΠ»ΠΈ ΡƒΠ΄Π°Π»Π΅Π½ΠΎ.
  • Вся Π±Π°Π·Π°, состоящая ΠΈΠ· ΠΏΠΎΡ‡Ρ‚ΠΈ ΠΏΠΎΠ»ΡƒΠΌΠΈΠ»Π»ΠΈΠΎΠ½Π° статСй, вСсит 2.95 Π“Π±. Π’ сТатом Π²ΠΈΠ΄Π΅ β€” 495 Мб.
  • ВсСго Π½Π° Π₯Π°Π±Ρ€Π΅ Π°Π²Ρ‚ΠΎΡ€Π°ΠΌΠΈ ΡΠ²Π»ΡΡŽΡ‚ΡΡ 37804 Ρ‡Π΅Π»ΠΎΠ²Π΅ΠΊ. Напоминаю, Ρ‡Ρ‚ΠΎ это статистика Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΈΠ· ΠΆΠΈΠ²Ρ‹Ρ… постов.
  • Π‘Π°ΠΌΡ‹ΠΉ ΠΏΡ€ΠΎΠ΄ΡƒΠΊΡ‚ΠΈΠ²Π½Ρ‹ΠΉ Π°Π²Ρ‚ΠΎΡ€ Π½Π° Π₯Π°Π±Ρ€Π΅ β€” alizar β€” 8774 ΡΡ‚Π°Ρ‚ΡŒΠΈ.
  • Π‘Ρ‚Π°Ρ‚ΡŒΡ с самым большим Ρ€Π΅ΠΉΡ‚ΠΈΠ½Π³ΠΎΠΌ β€” 1448 плюсов
  • Бамая читаСмая ΡΡ‚Π°Ρ‚ΡŒΡ β€” 1660841 просмотров
  • Бамая обсуТдаСмая ΡΡ‚Π°Ρ‚ΡŒΡ β€” 2444 коммСнтария

Ну ΠΈ Π² Π²ΠΈΠ΄Π΅ Ρ‚ΠΎΠΏΠΎΠ²Π’ΠΎΠΏ 15 Π°Π²Ρ‚ΠΎΡ€ΠΎΠ²Π’Π΅ΡΡŒ Π₯Π°Π±Ρ€ Π² ΠΎΠ΄Π½ΠΎΠΉ Π±Π°Π·Π΅
Π’ΠΎΠΏ 15 ΠΏΠΎ Ρ€Π΅ΠΉΡ‚ΠΈΠ½Π³ΡƒΠ’Π΅ΡΡŒ Π₯Π°Π±Ρ€ Π² ΠΎΠ΄Π½ΠΎΠΉ Π±Π°Π·Π΅
Π’ΠΎΠΏ 15 Ρ‡ΠΈΡ‚Π°Π΅ΠΌΡ‹Ρ…Π’Π΅ΡΡŒ Π₯Π°Π±Ρ€ Π² ΠΎΠ΄Π½ΠΎΠΉ Π±Π°Π·Π΅
Π’ΠΎΠΏ 15 ΠΎΠ±ΡΡƒΠΆΠ΄Π°Π΅ΠΌΡ‹Ρ…Π’Π΅ΡΡŒ Π₯Π°Π±Ρ€ Π² ΠΎΠ΄Π½ΠΎΠΉ Π±Π°Π·Π΅

Π˜ΡΡ‚ΠΎΡ‡Π½ΠΈΠΊ: habr.com