Untitled

mail@pastecode.io avatar
unknown
python
2 months ago
10 kB
2
Indexable
Never
import base64
import hashlib
import re
from threading import Thread
from threading import Lock
import subprocess,time
from random import randint
from random import randrange
import os
import socket
import MySQLdb
import urllib.parse

proxy_type = 'http' #socks5, http

if proxy_type == 'http':
	import requests
else:
	import requesocks as requests

num_threads = 10
myLock = Lock() #mysql
myLock2 = Lock() #schetchiki
i = 0

con = MySQLdb.connect(host="localhost", user="root", passwd="", db="wiki")
cur = con.cursor()
cur.execute('SET NAMES `utf8`')
con.autocommit(True)

#Так как я остановил предыдущий скрипт, то в базе могут остатьс записи со статусом inprogress, делаем их pending
cur.execute("UPDATE `wikipedia` SET status = 'pending' WHERE status='inprogress'")

def parse():
    global con
    global cur
    global myLock
    global i

    while True:
        myLock.acquire()
        try:
            #по умолчанию новые добавляемые записи в таблице имеют статус pending, это значит те которые нужно просканировать
            #всего три статуса pending, inprogress, done
            #берем одну запись со статусом pending
            cur.execute("SELECT id, url FROM `wikipedia` WHERE status = 'pending' AND lang = 'ru' LIMIT 1")
        except:            
            pass    
        result = cur.fetchall()    
        if result:    
            try:
                #так как программа многопоточна, то другой поток может взять в работу эту же запись в базе
                #чтобы такого не было, помечаем запись как inprogress
                cur.execute("UPDATE `wikipedia` SET status = 'inprogress' WHERE id=" + str(result[0][0]))
            except:            
                pass
        myLock.release()
        
        if result:
            #если есть задача хотябы одна, то начинаем ее выполнять
            #получаем хтмл код вики страницы
            html = file_get_contents(result[0][1])
            #парсим все ссылки на странице (в HTML коде)
            links = parse_links(result[0][1], html)
            #в переменно links находятся все ссылки из HTML кода, перебираем их в цикле
            for link in links:
                #регулярными выражениями проверяем является ссылка внешней или внутренней
                #если ссылка содержит wikipedia.org то она внутренняя, иначе - внешняя
                p = re.compile("https:\/\/[^\/]*wikipedia.org")
                m = p.search(link)
                if m:
                    #ссылка является внутренней
                    #определяем язык внутренней ссылки википедии
                    # например функция вернет ru для ссылки https://ru.wikipedia.org/wiki/%D0%A1%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%B0
                    # то есть ru это то что находится между 'https://' и 'wikipedia.org'
                    lang = get_lang_wiki(link)

                    myLock.acquire()
                    try:
                        #провеярем есть ли эта внутренняя ссылка в базе
                        cur.execute("SELECT id FROM `wikipedia` WHERE url = '" + con.escape_string.decode("utf-8")(link) + "'")
                    except:
                        pass
                    result2 = cur.fetchall()
                    if not result2:
                        try:
                            #если такой ссылки нет в базе, то заносим ее в базу
                            #указывая url и lang, кстати поле status проставится pending по умолчанию
                            #можно так базу проектировать чтобы при добавлении новых записей поле имело значение по умолчанию
                            #напомню pending - это означает что задачу нужно сделать, а done что задача сделана
                            #то есть все добавляемые новые ссылки со статусом pending (нужно сделать)
                            cur.execute("INSERT INTO `wikipedia` (`url`, `lang`) VALUES ('" + con.escape_string(link).decode("utf-8") + "', '" + lang + "')")
                        except:
                            pass
                    myLock.release()
                else:
                    #ссылка является внешней
                    # определяем зону домена
                    # Функция принимает на вход ссылку, наример https://www.bing.com/maps/default.aspx
                    # и возвращает доменную зону com
                    zone = get_zone(link)
                    myLock.acquire()
                    try:
                        #вставляем в базу внешнюю ссылку
                        cur.execute("INSERT INTO `links` (`wikipedia_id`, `url`, `zone`) VALUES (" + str(result[0][0]) + ", '" + con.escape_string(link).decode("utf-8") + "', '" + zone + "')")
                    except:
                        pass
                    myLock.release()

            myLock.acquire()
            try:
                #мы закончили выполнять текущую задачу, помечаем ее как done
                cur.execute("UPDATE `wikipedia` SET status = 'done' WHERE id=" + str(result[0][0]))
            except:
                pass
            myLock.release()
            myLock2.acquire()
            i += 1
            myLock2.release()
        else:
            #если задач в базе нет, то спим 5 секунд, и через 5 секунд проверяем снова есть ли задачи
            time.sleep(5)

#функция принимает на вход урл, например https://ru.wikipedia.org/wiki/%D0%A1%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%B0
#т возвращет HTML код этой страницы
def file_get_contents(url):
    html = ''
    try:
        headers = {
            'User-Agent': "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/W.X.Y.Z Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)",
            'Accept': "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
            'Accept-Language': "ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3",
        }

        session = requests.Session()
        response = session.post(url,
                                headers=headers,
                                allow_redirects=True, timeout=5)
        html = response.text
    except:
        pass
    return html

#Функция принимает на вход HTML код и возвращает все ссылки на этой странице (внутренние и внешние)
def parse_links(base_url, html):
    p = re.compile("<a[^>]+href=('|\")([^'\"]+)('|\")")
    links = re.findall(p, html)

    absolute_links = []
    for link in links:
        absolute_links.append(urllib.parse.urljoin(base_url, link[1]))
    return absolute_links

#функция возвращает язык внутренней страницы википедии
#например функция вернет ru для ссылки https://ru.wikipedia.org/wiki/%D0%A1%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%B0
#то есть ru это то что находится между 'https://' и 'wikipedia.org'
def get_lang_wiki(link):
    lang = 'en'
    m = re.search('https:\/\/([^\.]+)\.wikipedia.org', link)
    if m:
        lang = m.group(1)
    return lang

#определяем зону домена
#Функция принимает на вход ссылку, наример https://www.bing.com/maps/default.aspx
#и возвращает доменную зону com
def get_zone(link):
    m = re.search('https?:\/\/[^\/]+\.([a-z]+)', link)
    if m:
        return m.group(1)
    else:
        return ''

def showstat():
    global con
    global cur
    global myLock
    global i
    while True:
        myLock.acquire()
        try:
            #Достань кол-во записей в таблице wikipedia у которых статус done (выполнены)
            cur.execute("SELECT COUNT(*) FROM `wikipedia` WHERE status='done'")
        except:
            pass
        result_wikipedia_done = cur.fetchall()
        try:
            #Достань кол-во записей в таблице wikipedia
            cur.execute("SELECT COUNT(*) FROM `wikipedia`")
        except:
            pass
        result_wikipedia = cur.fetchall()
        try:
            # Достань кол-во записей в таблице links
            cur.execute("SELECT COUNT(*) FROM `links`")
        except:
            pass
        result_links = cur.fetchall()
        myLock.release()

        print("position: " + str(i) + ", wikipedia done: " + str(result_wikipedia_done[0][0]) + ", wikipedia total: " + str(result_wikipedia[0][0]) + ", links: " + str(result_links[0][0]))
        time.sleep(5)


worker1 = Thread(target=showstat)
worker1.daemon = True
worker1.start()

for i in range(num_threads):
    worker = Thread(target=parse)
    worker.daemon = True
    worker.start()

worker.join()
worker1.join()

#c:\Python311\python d:\python\wiki\main.py
Leave a Comment