Статья Пишем парсер на Python - грабим Proxy ч.2

Продолжим начатое.
В прошлой СТАТЬЕ мы спарсили список прокси, и получили очищенный от мусора вывод в виде словаря. Чтобы совсем всё получилось идеально (убрать кавычки и фигурные скобки), нужно словарь перевести в строку.

Для этого напишем цикл, и разместим его сразу под словарём data. Чтобы цикл был рабочим, нужно переменную b сделать глобальной.

Python:
global  b
        d = " "
        for i in data:            
            b = data[i]
            c = i+": "+data[i]
            d = d+c+', '

Можно также убрать пробел в начале записи с помощью lstrip(), и вывод на печать будет такой print(d.lstrip())

Запускаем программу, и получаем чистый список данных в виде строк

exce.png


Но конечно лучше же иметь запись результатов в текстовом файле. Поэтому мы поменяем вывод на запись в файл и получим на выходе proxy.txt

Python:
with open('proxy.txt', 'a') as f:
            print(d, file=f)

Текстовый файл это уже замечательно, но мы сделаем наши данные ещё более читаемыми. Для этого откроем proxy.txt с помощью EXEL. Выделим щелчком мышки столбец А, зайдём во вкладку "Данные" --> "Текст по столбцам"

ex.png


Жмём далее, выставляем разделитель "запятая"

ex2.png


Ещё раз далее и ОК. Расставим ширину колонок как нам нравится и получим удобный и красивый результат. Но как говорится не все прокси одинаково хороши. Нам нужно отобрать наилучшие. Поэтому отсортируем наш лист по колонке D и получим в самом низу сервера с низкой анонимностью.

excel.png


Теперь просто-напросто выделим строки с такими серверами и удалим. А затем снова отсортируем по столбцу F, и увидим следующий результат

excel1.png


Теперь можно сохранить XLS, и пользоваться самыми быстрыми прокси (колонка F). В колонке D выбираем при этом варианты с максимальной анонимностью. В данном примере самыми лучшими соответственно будут сервера в строках 1, 3, 6, 7, 8.

Ну вот мы спарсили и отобрали лучшие сервера. Работу с парсингом по этому сайту можно считать законченной. Там есть ещё другие странички с серверами, но мы их не будем парсить, нам же нужны только самые свежие сервера. Списки прокси обновляются каждый день, так что можно запускать программу раз в день, отбирать лучшие варианты в EXCEL и пользоваться.

Python:
import requests
from bs4 import BeautifulSoup


def get_html(site):
    r = requests.get(site)
    return r.text


def get_page_data(html):
    soup = BeautifulSoup(html, 'lxml')
    line = soup.find('table', id='theProxyList').find('tbody').find_all('tr')

    for tr in line:
        td = tr.find_all('td')
        ip = td[1].text
        port = td[2].text
        country = td[3].text.replace('\xa0', '')
        anonym = td[4].text.replace('\r\n        ', '')
        types = td[5].text.replace('\r\n\t\t\t\t\t', '').replace('\r\n        ', '')
        time = td[6].text

        data = {'ip': ip,
                'Порт': port,
                'Страна': country,
                'Анонимность': anonym,
                'Тип': types,
                'Время отклика': time}

        global  b
        d = " "
        for i in data:            
            b = data[i]
            c = i+": "+data[i]
            d = d+c+', '

        with open('proxy.txt', 'a') as f:
            print(d, file=f)


def main():
    url = 'http://foxtools.ru/Proxy'
    get_page_data(get_html(url))


if __name__ == '__main__':
    main()
А я не прощаюсь, и напишу скоро ещё какую-нибудь программку :cool:
 
Последнее редактирование:

explorer

Platinum
05.08.2018
1 080
2 475
BIT
0
Сегодня я решил немного переписать парсер и сделать вывод как в оригинале с шапкой сверху.

or.png


Для этого нужно заглянуть в код шапки. Как видно шапка заключена в тег 'thead'

or2.png


Также есть теги 'tr' и 'th'

or3.png


Соответственно нужно создать для шапки отдельную переменную head.

Python:
head = soup.find('thead').find_all('tr')

Теперь напишем для шапки отдельный цикл и на этот раз данные мы разместим не в словаре, а в кортеже

Python:
for tr in head:
        th = tr.find_all('th')
        ip1 = th[1].text
        port1 = th[2].text
        country1 = th[3].text.replace('\xa0', '')
        anonym1 = th[4].text.replace('\r\n        ', '')
        types1 = th[5].text.replace('\r\n\t\t\t\t\t', '').replace('\r\n        ', '')
        time1 = th[6].text

        data1 = (ip1, port1, country1, anonym1, types1, time1)

Если сейчас вывести на печать, получим такой результат

cort.png


Наша конструкция сработала, шапка есть. Но присутствуют скобки и кавычки. От них легко избавиться, если печать вывести таким образом

Python:
print(", ".join([str(s) for s in data1]))

Теперь данные получились чистыми

cort2.png


Наведёт окончательную красоту - результат скопируем в EXCEL, также сначала распределим по столбцам. И чтобы шапочку всегда было видно, нужно её закрепить.

cort3.png


Готово! Теперь, если мы прокрутим в самый низ, то всё равно шапка будет оставаться на месте.

cort4.png


Python:
import requests
from bs4 import BeautifulSoup


def get_html(site):
    r = requests.get(site)
    return r.text


def get_page_data(html):
    soup = BeautifulSoup(html, 'lxml')
    head = soup.find('thead').find_all('tr')
    line = soup.find('table', id='theProxyList').find('tbody').find_all('tr')

    for tr in head:
        th = tr.find_all('th')
        ip1 = th[1].text
        port1 = th[2].text
        country1 = th[3].text.replace('\xa0', '')
        anonym1 = th[4].text.replace('\r\n        ', '')
        types1 = th[5].text.replace('\r\n\t\t\t\t\t', '').replace('\r\n        ', '')
        time1 = th[6].text

        data1 = (ip1, port1, country1, anonym1, types1, time1)
        print(", ".join([str(s) for s in data1]))

        for tr in line:
            td = tr.find_all('td')
            ip = td[1].text
            port = td[2].text
            country = td[3].text.replace('\xa0', '')
            anonym = td[4].text.replace('\r\n        ', '')
            types = td[5].text.replace('\r\n\t\t\t\t\t', '').replace('\r\n        ', '')
            time = td[6].text

            data = (ip, port, country, anonym, types, time)
            print(", ".join([str(s) for s in data]))


def main():
    url = 'http://foxtools.ru/Proxy'
    get_page_data(get_html(url))


if __name__ == '__main__':
    main()
Надеюсь, вы что-то узнали для себя нового :)
 
D

Dwight Schrute

Я переписал примерно все))

  • Добавил управление скриптом через аргументы командной строки
Код:
(venv) [23:55:27]> ./proxy_parser.py --help

usage: proxy_parser.py [-h] -p PAGE_NUMBERS [-o OUTPUT] [-d DELAY]

Parse proxy servers from foxtools.ru

optional arguments:
  -h, --help            show this help message and exit
  -p PAGE_NUMBERS, --page-numbers PAGE_NUMBERS
                        Max pages (default: None)
  -o OUTPUT, --output OUTPUT
                        Output csv path (default: proxy)
  -d DELAY, --delay DELAY
                        Delay between requests in seconds (default: 2)
  • Упростил парсинг HTML
  • Добавил возможность прокачки нескольких страниц за раз в один файл
  • Сделал вывод в файл с помощью стандартной библиотеки csv
  • Декомпозировал код на несколько мелких функций

Python:
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from __future__ import print_function

import argparse
import requests
import csv
import time

from bs4 import BeautifulSoup

URL = "http://foxtools.ru/Proxy"


class DownloadHTMLError(Exception):
    pass


def read_args():
    parser = argparse.ArgumentParser(description="""Parse proxy servers from foxtools.ru""",
                                     formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument("-p", "--page-numbers", default=1, type=int, help="Max pages")
    parser.add_argument("-o", "--output", default="proxy", help="Output csv path")
    parser.add_argument("-d", "--delay", default=2, type=int, help="Delay between requests in seconds")
    return parser.parse_args()


def make_http_req(url, page_num, delay):
    """
    Функция отвечает за скачивание HTML
    :param url: строка
    :param page_num: целое число обозначающее номер страницы
    :param delay: целое число обозначающее задержку (в секундах) между запросами, если запрашивается несколько страниц,
    чтобы избежать возможного бана.
    :return: html
    """
    params = {"page": page_num}
    try:
        resp = requests.get(url, params=params)
        print("Fetch url {} - {}".format(resp.url, "OK" if resp.ok else resp.status_code))
        resp.raise_for_status()
        if delay:
            time.sleep(delay)
        return resp.text
    except requests.RequestException as e:
        print(e)


def parse_content(table):
    """
    Функия принимает объект BeautifulSoup, который содержит в себе только таблицу
    :param table: BeautifulSoup объект
    :return: список словарей, где каждый словарь представляет собой описание одного прокси сервера
    """
    tr = table.find("tbody").find_all("tr")
    content = []
    for t in tr:
        td = t.find_all("td")
        data = {
            "ip": td[1].find(text=True).strip().encode("utf-8"),
            "port": td[2].find(text=True).strip().encode("utf-8"),
            "country": td[3].find(text=True).strip().encode("utf-8"),
            "anonymous": td[4].find(text=True).strip().encode("utf-8"),
            "type": td[5].find(text=True).strip().encode("utf-8"),
            "response_time": td[6].find(text=True).strip().encode("utf-8"),
            "check_datetime": td[7].find(text=True).strip().encode("utf-8"),
        }

        content.append(data)
    return content


def main():
    cli_args = read_args()

    result = []
    for page_num in range(1, cli_args.page_numbers + 1):
        html = make_http_req(URL, page_num, cli_args.delay)
        if not html:
            raise DownloadHTMLError("Bad page number or some server error")

        soup = BeautifulSoup(html, "lxml")
        table = soup.find("table", {"id": "theProxyList"})

        result.extend(parse_content(table))

    with open(cli_args.output, "w") as f:
        fieldnames = ["ip", "port", "country", "anonymous", "type", "response_time", "check_datetime"]
        writer = csv.DictWriter(f, fieldnames=fieldnames)

        writer.writeheader()
        writer.writerows(result)


if __name__ == "__main__":
    main()

(venv) [00:02:05]> cat proxy.csv
ip,port,country,anonymous,type,response_time,check_datetime
190.122.15.1,4444,Аргентина (AR),низкая,HTTP,1.4,26.10.2018 в 14:27
200.237.249.55,80,Бразилия (BR),наивысшая,HTTP,0.26,26.10.2018 в 13:36
107.151.152.210,80,США (US),наивысшая,HTTP,0.18,26.10.2018 в 01:10
212.200.126.14,8080,Сербия (rs),наивысшая,HTTPS,1.78,26.10.2018 в 00:34
197.220.193.201,8080,Замбия (ZM),наивысшая,HTTPS,11.85,25.10.2018 в 00:52
46.16.226.10,8080,Россия (RU),наивысшая,HTTPS,10.69,25.10.2018 в 00:21
162.144.57.157,80,Австралия (AU),наивысшая,HTTP,0.19,24.10.2018 в 00:21
115.124.75.149,80,Индонезия (ID),низкая,HTTP,0.39,23.10.2018 в 12:18
212.227.159.39,80,Германия (DE),наивысшая,HTTP,0.01,23.10.2018 в 12:17
178.238.229.236,80,Германия (DE),высокая,HTTP,0.08,23.10.2018 в 12:12
104.236.51.165,8080,США (US),наивысшая,HTTP,0.31,23.10.2018 в 12:09
139.162.182.113,80,Нидерланды (NL),наивысшая,HTTP,0.02,23.10.2018 в 01:12
81.46.212.102,80,Испания (ES),наивысшая,HTTP,0.07,23.10.2018 в 01:10
202.103.215.23,2226,Китай (CN),наивысшая,HTTP,0.43,23.10.2018 в 00:53
5.45.64.97,3128,Люксембург (LU),средняя,HTTP,1.38,23.10.2018 в 00:53
211.150.65.29,80,Китай (CN),наивысшая,HTTP,0.11,23.10.2018 в 00:39
200.202.240.174,80,Бразилия (BR),средняя,HTTP,0.51,23.10.2018 в 00:13
124.124.221.29,80,Индия (IN),наивысшая,HTTP,0.37,23.10.2018 в 00:12
94.230.35.108,80,Россия (RU),наивысшая,HTTP,0.14,23.10.2018 в 00:11
111.207.167.220,80,Китай (CN),наивысшая,HTTP,0.21,23.10.2018 в 00:05
218.108.168.74,80,Китай (CN),наивысшая,HTTP,0.19,22.10.2018 в 23:54
62.141.46.151,80,Германия (DE),наивысшая,HTTP,0.02,22.10.2018 в 23:54
124.127.51.140,80,Китай (CN),наивысшая,HTTP,0.1,22.10.2018 в 23:54
118.97.209.61,8080,Индонезия (ID),низкая,HTTP,0.38,22.10.2018 в 23:52
173.236.245.69,80,США (US),наивысшая,HTTP,0.12,22.10.2018 в 23:51
173.212.205.14,80,США (US),наивысшая,HTTP,0.02,22.10.2018 в 23:51
218.108.168.173,80,Китай (CN),наивысшая,HTTP,0.3,22.10.2018 в 23:51
125.162.44.139,80,Индонезия (ID),низкая,HTTP,0.66,22.10.2018 в 12:23
37.187.16.186,80,Австрия (AT),наивысшая,HTTP,0.02,22.10.2018 в 12:20
213.209.107.179,80,Германия (DE),высокая,HTTP,0.02,22.10.2018 в 12:20
 

explorer

Platinum
05.08.2018
1 080
2 475
BIT
0
Dwight Schrute прекрасно что приняли участие в кодинге парсера. Завтра повнимательнее посмотрю ваш код.
Вывод конечно нужно чуток отформатировать - порт через двоеточие и пробелы после запятых поставить. Сейчас посплю и потом ещё один интересный код выложу, надеюсь не пройдёте мимо.
 
D

Dwight Schrute

Можно еще доработать скрипт и парсить количество страниц сверху таблицы, чтобы не задавать число руками. Но есть еще более простой путь, это использовать их , о чем написано в HTML коде
<!--Зачем парсить, когда есть бесплатный API, выдающий данные без ограничений в удобном формате (xml, json, text) и большем количестве-->
 

explorer

Platinum
05.08.2018
1 080
2 475
BIT
0
использовать их , о чем написано в HTML коде
Конечно я это видел, молодцы, улыбнула закомментированная фраза)
Глянул ваш код, вместо 7 раз в data .encode("utf-8"), можно один раз сделать это здесь
with open(cli_args.output, "w", encoding='utf-8') as f:
 
  • Нравится
Реакции: Dwight Schrute
D

Dwight Schrute

Да, только это справедливо для Python3, на 2.7 - это работать не будет. Я когда писал скрипт использовал ранее созданный venv c python2.7.
 

Rand0m_M

Green Team
16.04.2018
33
28
BIT
1
Столкнулся с тем что не все прокси доступны из сграбленоо списка, добавил проверку прокси на доступность, доступные прокси записываются в файл форматом 104.236.51.165:8080. Такой список потом удобно использовать в библиотеке requests. Вывод раскрашен цветами, добавил colorama для отображения цветов в виндовс, правда под виндой не проверял, по идее должно раскрасить.

Python:
import requests
from bs4 import BeautifulSoup
import socket
from colorama import init
init()

def get_html(site):
    r = requests.get(site)
    return r.text


def get_page_data(html):
    soup = BeautifulSoup(html, 'lxml')
    head = soup.find('thead').find_all('tr')
    line = soup.find('table', id='theProxyList').find('tbody').find_all('tr')

    for tr in head:
        th = tr.find_all('th')
        ip1 = th[1].text
        port1 = th[2].text
        country1 = th[3].text.replace('\xa0', '')
        anonym1 = th[4].text.replace('\r\n        ', '')
        types1 = th[5].text.replace('\r\n\t\t\t\t\t', '').replace('\r\n        ', '')
        time1 = th[6].text

        data1 = (ip1, port1, country1, anonym1, types1, time1)
        print(", ".join([str(s) for s in data1]))
        
        for tr in line:
            td = tr.find_all('td')
            ip = td[1].text
            port = td[2].text
            country = td[3].text.replace('\xa0', '')
            anonym = td[4].text.replace('\r\n        ', '')
            types = td[5].text.replace('\r\n\t\t\t\t\t', '').replace('\r\n        ', '')
            time = td[6].text

            data = (ip, port, country, anonym, types, time)
            print(", ".join([str(s) for s in data]))
            
            ### проверка порта прокси   
            s = socket.socket()
            s.settimeout(4) ### сколько ждать соеденения с портом прокси
            print('try proxy\033[93m',ip+':'+port,'\033[00m')
            try:
                s.connect((ip,int(port)))
            except socket.error:
                s.close()
                print('port closed! proxy \033[31m'+ip+':'+port,'!UNAVAILABLE!\033[00m')
            else:
                s.close()
                with open ('proxy_list_checked0.txt','a') as fa:
                    print('proxy:\033[32m',ip+':'+port,'AVAILABLE\033[00m and written to file')
                    fa.write(ip+':'+port+'\n')


def main():
    url = 'http://foxtools.ru/Proxy'
    get_page_data(get_html(url))


if __name__ == '__main__':
    main()
 
  • Нравится
Реакции: Andre6381 и explorer

explorer

Platinum
05.08.2018
1 080
2 475
BIT
0
Столкнулся с тем что не все прокси доступны из сграбленоо списка, добавил проверку прокси на доступность, доступные прокси записываются в файл форматом 104.236.51.165:8080. Такой список потом удобно использовать в библиотеке requests. Вывод раскрашен цветами, добавил colorama для отображения цветов в виндовс, правда под виндой не проверял, по идее должно раскрасить.

Ну что же, прекрасное дополнение!
Цвета через colorama всегда в windows работают, в отличии от termcolor.

Добавлю только что нужно тогда в конец кода добавить input(), чтобы можно было спокойно посмотреть весть лог. Иначе зачем выводить инфу и раскрашивать?
 
Последнее редактирование:
  • Нравится
Реакции: Rand0m_M и Andre6381
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!