1. BURMALDA27.07.2024 в 19:26от
  2. Adark27.07.2024 в 18:16от
Загрузка...

Web scraping with python (blacklist example)

Тема в разделе "Web-программирование", создана пользователем Meme Machine, 09.05.2018.

  1. Meme Machine

    Любитель трепать не по делу
    Meme Machine

    Статус:
    Оффлайн
    Регистрация:
    25.11.16
    Сообщения:
    359
    Репутация:
    699 +/-
    Депозит:
    2000
    Надеюсь разделом не ошибся.
    Веб скрапинг позволяет получать нам необходимые данные со страницы, путем парсинга HTML, это основной и верный способ.

    Для комфортного скрапинга информации, используют в Python:
    • Scrapy
    • Beautiful Soup
    • Requests
    • Selenium
    Scrapy довольно сложна для новичка, как для меня, например, на первых этапах, когда у BS идет из коробки все довольно просто.

    Requests помогает строить грамотно и просто запросы к серверу

    Selenium необходим, когда у сайта динамический контент (привет AJAX и всяким React с Virtual DOM). Сам BS или Scrapy с этим может справится, но все довольно криво и запутано, так что Selenium лучший из вариантов реализации, но и самый долгий. Ведь по-сути, он нужен для тестирования, но иногда и в этом пригодится. Грубо говоря, с помощью Selenium мы эмулируем действия человека (запускаем браузер, переходим на страницу, открываем и тд). Все это позволяет обойти/загрузить контент.

    Я приведу вам простой пример работы, т.к. моя дипломная связана с выгрузкой данных из ресурсов, в начале пути я баловался с BS + Requests связкой, а именно, распарсил темы в BlackList у одного борда ( lol z team), собрал информацию со всех тем, где был забанен виновный, отсортировал, попытался найти контакты, кошельки и прочее.

    Но, мы будем рассматривать только первые 20 тем, ведь нам этого достаточно для анализа, 20 тем - это 2-3 дня, иногда и больше.

    Сама авторизация у меня на сайте вызвала проблемы, из-за особенности куки и самой сессии, невидимых полей при авторизации (3 hidden поля, которые они проверяют при авторизации) и т.д.

    Первым делом, если вы не Питонист, то установите Питон, да, глупо звучит, но без него адекватно проверить код вы не сможете.

    Для работы, нам необходимо:
    Requests
    Beautiful Soup
    Fake UserAgent

    Fake UserAgent:

    Я сразу прикладываю весь код, если кому - то лень/не интересно/я все знаю.

    ******* - lol z team

    Код:
    import requests
    import re
    
    from bs4 import BeautifulSoup, NavigableString, Tag
    from fake_useragent import UserAgent
    
    
    class BlackList:
        def __init__(self):
            self.user_agent = UserAgent()
    
            self.header = {
                'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
                'Accept-Encoding': 'gzip, deflate',
                'Accept-Language': 'ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3',
                'Connection': 'keep-alive',
                'X-Requested-With': 'XMLHttpRequest'
            }
    
            self.auth_lolz = 'https://********.net/login'
            self.blacklist_lolz = 'https://********.net/forums/774/'
            self.main_lolz = 'https://********.net/'
    
            with requests.Session() as s:
                req = s.get(self.blacklist_lolz)
    
                soup = self.beautifulsoup(req.content)
    
                divs_all = soup.find_all('a', class_='PreviewTooltip ')
    
                links_all = [div['href'] for div in divs_all]
    
                for link in links_all:
                    number_list = []
                    link_page = requests.get(self.main_lolz + link)
                    link_soup = self.beautifulsoup(link_page.content)
    
                    div_tabs = link_soup.find('blockquote', class_='messageText SelectQuoteContainer ugc baseHtml')
                    banned = link_soup.find('span', class_='style18 is_banned')
    
                    if banned:
                        try:
                            user = div_tabs.find('a', class_='username')['href']
    
                        except:
                            user = 'None'
    
                        numbers = re.findall('(\d+)', str(div_tabs))
                        div_text = div_tabs.text.replace('\n', ' ').strip()
                        contact = [contact_elem for contact_elem in div_text.split(' ') if contact_elem.startswith('@')][1:]
                        br = div_tabs.find_all('br')
    
                        for br_elem in br[0:2]:
                            next = br_elem.nextSibling
    
                            if not (next and isinstance(next, NavigableString)):
                                continue
    
                            next_2 = next.nextSibling
    
                            if next_2 and isinstance(next_2, Tag) and next_2.name == 'br':
                                text = str(next).strip()
                                if text:
                                    print('MAIN INFO IN TEXT: ', text)
    
                        for number in numbers:
                            if len(number) > 10:
                                number_list.append(number)
    
                        print('URL: ', self.main_lolz + link)
                        print('TEXT ALL: ', div_tabs.text)
                        print('URL USER IN BLACKLIST: ', user)
                        print('ALL NUMBERS IN TEXT: ', number_list)
                        print('CAN BE A TELEGRAM: ', contact)
                        print('------------------------------')
                        print('\n')
    
        def beautifulsoup(self, url_content):
                return BeautifulSoup(url_content, 'lxml')
    
    
    if __name__ == '__main__':
        app = BlackList()

    Да, программа простая, код маленький.
    Да, я немного не правильно построил __init__ в классе.

    Код:
            self.header = {
                'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
                'Accept-Encoding': 'gzip, deflate',
                'Accept-Language': 'ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3',
                'Connection': 'keep-alive',
                'X-Requested-With': 'XMLHttpRequest'
            }
    Наш хедер, который нужен для того, что робота не спалили, грубо говоря, при запросе к серверу.

    Код:
            self.auth_lolz = 'https://********.net/login'
            self.blacklist_lolz = 'https://********.net/forums/774/'
            self.main_lolz = 'https://********.net/'
    
    Наши основные ссылки для парсинга, первая не несет в себе уже особой надобности.
    Вторая - сам раздел Арбитража на борде
    Третья - сам сайт

    Напомню, если вы видите ****** - lol z team.

    Запуск программы возможен и без открытия сессии

    Код:
     with requests.Session() as s:
    
    Код:
                req = s.get(self.blacklist_lolz)
    
                soup = self.beautifulsoup(req.content)
    
    Мы отправляем GET - запрос к серверу, получаем ответ и с помощью метода beautifulsoup

    Код:
        def beautifulsoup(self, url_content):
                return BeautifulSoup(url_content, 'lxml')
    Парсим нашу страницу

    Код:
                divs_all = soup.find_all('a', class_='PreviewTooltip ')
    
                links_all = [div['href'] for div in divs_all]
    Мы находим все a - теги в разметке HTML с классом PreviewTooltip, далее, с помощью упрощенной записи цикла, закидываем все элементы в список (list).

    Посмотреть вложение 24410

    И уже нашим циклом, for link in links_all проходим по каждому элементу в списке, то есть, по каждой ссылке. А зачем? А чтобы найти тех, кто уже забанен и спарсить инфу о них (правда кривоватую).

    Код:
                    number_list = []
                    link_page = requests.get(self.main_lolz + link)
                    link_soup = self.beautifulsoup(link_page.content)
    Мы формируем новый список, который будет отвечать за все числовые значения в тексте, дабы постараться найти реквизиты обманувшего. Ведь в разметке, весь текст будет под одним тегом.
    Далее, просто, как и выше, делаем GET - запрос, парсим и получаем HTML кусок нашей страницы, с которым и должны работать.

    Код:
                    div_tabs = link_soup.find('blockquote', class_='messageText SelectQuoteContainer ugc baseHtml')
                    banned = link_soup.find('span', class_='style18 is_banned')
    Сейчас мы должны определить, является ли человек забаненым или решили проблему. Мы ищем нужный класс в нужном куске разметки.

    Посмотреть вложение 24410


    Далее, проверка if banned означает, собственно, что человек в бане или нет. Мы рассматриваем простую булевую проверку True/False.

    Код:
                        try:
                            user = div_tabs.find('a', class_='username')['href']
    
                        except:
                            user = 'None'
    В try/except мы оборачиваем проверку ссылки на пользователя, да, бывает что нет ее (я не знаю почему), да, есть более современная проверка.

    Код:
                        numbers = re.findall('(\d+)', str(div_tabs))
    
    С помощью либы re и более удобного поиска чисел в тексте, мы собственно их и находим. Для дальнейшей работы с ними, дабы найти кошелек человека.

    Код:
                        div_text = div_tabs.text.replace('\n', ' ').strip()
                        contact = [contact_elem for contact_elem in div_text.split(' ') if contact_elem.startswith('@')][1:]
    Изначально, текст может представлять из себя кашу с всякими \n и прочее. Мы чистим текст от лишних знаков для удобного прочтения.

    Код:
                        br = div_tabs.find_all('br')
    
    Больше жертв богу верстки, да, весь раздел текста у них в br - ах :russian_roulette:
    Что поделать, мы ищем все br, а зачем? А потому что, ссылка на человека, например, между 1-2 br, основная информация между 3-4, например, думаю примерно ясно.

    Посмотреть вложение 24411

    И уже в цикле ищем все необходимое. А именно, выводим основную информацию текста, убирая лишнее.

    Код:
       for br_elem in br[0:2]:
                            next = br_elem.nextSibling
    
                            if not (next and isinstance(next, NavigableString)):
                                continue
    
                            next_2 = next.nextSibling
    
                            if next_2 and isinstance(next_2, Tag) and next_2.name == 'br':
                                text = str(next).strip()
                                if text:
                                    print('MAIN INFO IN TEXT: ', text)
    Код:
                        for number in numbers:
                            if len(number) > 10:
                                number_list.append(number)
    Простой цикл для поиска всех чисел, в уже списке с числами, где кол-во знаков больше 10, например, ведь кошельки имеют примерно столько символов.

    Ну и просто принтами, да, как особо умные, выводим информацию.

    Сама работы программы в основном стабильная, да, могут быть проблемы, писал ее для экспериментов, но вообще, немного пригодилась, помогла быстрее и удобнее искать мошенников с соседнего сайта.

    На сам парсинг уйдет порядка 2-10 секунд.

    Примеры вывода

    Посмотреть вложение 24412

    Посмотреть вложение 24413

    Посмотреть вложение 24414

    В дальнейшем, если будет время, оберну в декстоп Qt5 и покажу пример с Selenium.
     
      Deviance и Limphor нравится это.
  2. Meme Machine

    Любитель трепать не по делу
    Meme Machine

    Статус:
    Оффлайн
    Регистрация:
    25.11.16
    Сообщения:
    359
    Репутация:
    699 +/-
    Депозит:
    2000
    Да, немного коряво, говорю же, баловался.
     
  3. Meme Machine

    Любитель трепать не по делу
    Meme Machine

    Статус:
    Оффлайн
    Регистрация:
    25.11.16
    Сообщения:
    359
    Репутация:
    699 +/-
    Депозит:
    2000
    Я не стал особо заморачиваться, перед сном написал, работает - нормально :beach:
    Конечно, если бы я такое в дипломе писал, другое дело, веслом по голове и только
     
  4. Meme Machine

    Любитель трепать не по делу
    Meme Machine

    Статус:
    Оффлайн
    Регистрация:
    25.11.16
    Сообщения:
    359
    Репутация:
    699 +/-
    Депозит:
    2000
    Ну, если покажешь как лучше было, будет здорово.

    С регулярками у меня туго, мало сталкивался и мало с ними работал, если есть чет интересно почитать по ним, кидай сюда/под хайд/ в лс.
     
    Последнее редактирование: 09.05.2018