Pull to refresh

Пишем парсер на Java + MySQL

Reading time15 min
Views43K
Недавно пробегал на Хабре пост про базу доменных имен с электронной почтой. Решил написать парсер, чтоб благополучно слить всю эту базу. Но так как очень быстро сервис загнулся в силу хабраэффекта (а может админы пофиксили, черт его знает), я пошел дальше и нашел просто базу доменов в plaintext'е в зоне .RU. Решил ее пропарсить с помощью whois на nic.ru. Но на последнем действует скрипт, благополучно притормаживая слив базы с одного ip адреса. Выход — использование proxy листа. И, будучи благополучно задушенным жабой покупать прокси листы, я решил написать на Java два скрипта:
1. Парсит samair.ru/proxy и сливает в mysql прокси лист.
2. Проходит по базе и проверяет timeout полученных проксей.


База данных

Скрин структуры базы из PhpMyAdmin

Итак, сначала парсер.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;


public class ProxyHunter {

  /**
   @param args
   @throws ClassNotFoundException 
   @throws SQLException 
   @throws IOException 
   */
  public static void main(String[] argsthrows ClassNotFoundException, SQLException, IOException {
    // TODO Auto-generated method stub
    Class.forName("com.mysql.jdbc.Driver");
    //подключаем драйвер MySQL
    Connection conn = DriverManager.getConnection("jdbc:mysql://192.168.1.7:3306/database""username""password");
    //192.168.1.7 - имя хоста, можно подставить хоть localhost
    //database - имя базы данных для экспорта
    //username, password - имя пользователя и пароль для MySQL
    Statement st = conn.createStatement();
    URL connection = null;
    String[] replacements = new String[10];
    String host = null;
    String port = null;
    String anon_level = null;
    String country = null;
    int cursor = 0;
    HttpURLConnection urlconn = null;
    int n = 1;
    while (n <= 50)
    {
      if (n < 10)
      {
        connection = new URL("www.samair.ru/proxy/ip-address-0"; +n+".htm");
      }
      else
      {
        connection = new URL("www.samair.ru/proxy/ip-address-";+n+".htm");        
      }
      
      System.out.println("Starting page: "+Integer.toString(n));
      urlconn = (HttpURLConnectionconnection.openConnection();
      urlconn.setRequestMethod("GET");
      urlconn.connect();
      //посылаем GET запрос на список проксей samair'а
      java.io.InputStream in = urlconn.getInputStream();
      BufferedReader reader = new BufferedReader(new InputStreamReader(in));
      String text = null;
      String line = null;
      while ((line = reader.readLine()) != null)
      {
        text += line;
      }
      //парсим текст страницы
      replacements = text.substring(text.indexOf("<script src=\"http://samair.ru:81/js/m.js" type="text/javascript">"<script src=\"http://samair.ru:81/js/m.js" type="text/javascript">.length(), text.indexOf("</script></head>")).split(";");
      //на самаире, возможно, в целях защиты от парсеров порты в списке выводятся javascript'ом
      //в начале страницы рандомом задаются 10 переменных для каждой цыфры, затем они скриптом же и выводятся в таблицу
      //replacements - как раз массив этих переменных
      cursor = text.indexOf("<tr><td>");
      while (cursor != -1)
        {
        cursor += "<tr><td>".length();
        host = text.substring(cursor, text.indexOf("<script type=\"text/javascript\">", cursor));
        //host - адрес прокси сервера
        port = text.substring(text.indexOf(">document.write(\":\"+", cursor">document.write(\":\"+".length(), text.indexOf(")</script>" , cursor));
        port = removeChar(port, '+');
        for (int i = 0; i<10; i++)
        {
          port = port.replaceAll(replacements[i].split("=")[0], replacements[i].split("=")[1]);
          //подставляем вместо букв циферки
        }
        //port - порт сервера
        cursor = text.indexOf("</td><td>", cursor"</td><td>".length();
        anon_level = text.substring(cursor, text.indexOf("</td><td>", cursor));
        cursor = text.indexOf("</td><td>", cursor"</td><td>".length();
        cursor = text.indexOf("</td><td>", cursor"</td><td>".length();
        country = text.substring(cursor, text.indexOf("</td></tr>", cursor));
        //получаем остальную лабуду - тип сервера и страна, не пропадать же траффику зря) хотя они и вряд ли понадобятся
        ResultSet rs = st.executeQuery("select host, port from proxies where host = '"+host+"' and port = '"+port+"'");
        if (!rs.next())
        {
          st.executeUpdate("INSERT INTO proxies (host, port, anon_level, country) VALUES ('"+host+"', '"+port+"', '"+anon_level+"', '"+country+"')");
          System.out.println("Added: "+host+":"+port);
          //Если такого хоста и порта в базе еще нету, то вносим его туда
        }
        cursor = text.indexOf("<tr><td>", cursor);
        }
      
      n++;

    }
    
    st.close();
    conn.close();
  }
  
  public static String removeChar(String s, char c) {
       String r = "";
       for (int i = 0; i < s.length(); i ++) {
          if (s.charAt(i!= cr += s.charAt(i);
          }
       return r;
    }

}


И непосредственно сам чекер
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;


public class ProxyHunter {

  /**
   @param args
   @throws ClassNotFoundException 
   @throws SQLException 
   @throws IOException 
   */
  public static void main(String[] argsthrows ClassNotFoundException, SQLException, IOException {
    // TODO Auto-generated method stub
    Class.forName("com.mysql.jdbc.Driver");
    //подключаем драйвер MySQL
    Connection conn = DriverManager.getConnection("jdbc:mysql://192.168.1.7:3306/database""username""password");
    //192.168.1.7 - имя хоста, можно подставить хоть localhost
    //database - имя базы данных для экспорта
    //username, password - имя пользователя и пароль для MySQL
    Statement st = conn.createStatement();
    URL connection = null;
    String[] replacements = new String[10];
    String host = null;
    String port = null;
    String anon_level = null;
    String country = null;
    int cursor = 0;
    HttpURLConnection urlconn = null;
    int n = 1;
    while (n <= 100)
    {
      if (n < 10)
      {
        connection = new URL("www.samair.ru/proxy/ip-address-0";+n+".htm");
      }
      else
      {
        connection = new URL("www.samair.ru/proxy/ip-address-";+n+".htm");        
      }
      
      System.out.println("Starting page: "+Integer.toString(n));
      urlconn = (HttpURLConnectionconnection.openConnection();
      urlconn.setRequestMethod("GET");
      urlconn.connect();
      //посылаем GET запрос на список проксей samair'а
      java.io.InputStream in = urlconn.getInputStream();
      BufferedReader reader = new BufferedReader(new InputStreamReader(in));
      String text = null;
      String line = null;
      while ((line = reader.readLine()) != null)
      {
        text += line;
      }
      //парсим текст страницы
      replacements = text.substring(text.indexOf("<script src=\"http://samair.ru:81/js/m.js" type="text/javascript">"<script src=\"http://samair.ru:81/js/m.js" type="text/javascript">.length(), text.indexOf("</script></head>")).split(";");
      //на самаире, возможно, в целях защиты от парсеров порты в списке выводятся javascript'ом
      //в начале страницы рандомом задаются 10 переменных для каждой цыфры, затем они скриптом же и выводятся в таблицу
      //replacements - как раз массив этих переменных
      cursor = text.indexOf("<tr><td>");
      while (cursor != -1)
        {
        cursor += "<tr><td>".length();
        //host - адрес прокси сервера
        //каким-то непонятным китайским рандомом у самаира порт проксей выводится либо javascript'ом
        //либо plaintext'ом
        //обрабатываем
        if (text.indexOf(">document.write(\":\"+", cursor!= -1)
        {
          //это для javascript
          host = text.substring(cursor, text.indexOf("<script type=\"text/javascript\">", cursor));
          port = text.substring(text.indexOf(">document.write(\":\"+", cursor">document.write(\":\"+".length(), text.indexOf(")</script>" , cursor));
          port = removeChar(port, '+');
          for (int i = 0; i<10; i++)
          {
            port = port.replaceAll(replacements[i].split("=")[0], replacements[i].split("=")[1]);
            //подставляем вместо букв циферки
          }
        }
        else
        {
          //это plaintext
          host = text.substring(cursor, text.indexOf(":", cursor));
          port = text.substring(text.indexOf(":", cursor1, text.indexOf("</td><td>", cursor));
        }
        //port - порт сервера
        cursor = text.indexOf("</td><td>", cursor"</td><td>".length();
        anon_level = text.substring(cursor, text.indexOf("</td><td>", cursor));
        cursor = text.indexOf("</td><td>", cursor"</td><td>".length();
        cursor = text.indexOf("</td><td>", cursor"</td><td>".length();
        country = text.substring(cursor, text.indexOf("</td></tr>", cursor));
        //получаем остальную лабуду - тип сервера и страна, не пропадать же траффику зря) хотя они и вряд ли понадобятся
        ResultSet rs = st.executeQuery("select host, port from proxies where host = '"+host+"' and port = '"+port+"'");
        if (!rs.next())
        {
          st.executeUpdate("INSERT INTO proxies (host, port, anon_level, country) VALUES ('"+host+"', '"+port+"', '"+anon_level+"', '"+country+"')");
          System.out.println("Added: "+host+":"+port);
          //Если такого хоста и порта в базе еще нету, то вносим его туда
        }
        cursor = text.indexOf("<tr><td>", cursor);
        }
      
      n++;

    }
    
    st.close();
    conn.close();
  }
  
  public static String removeChar(String s, char c) {
       String r = "";
       for (int i = 0; i < s.length(); i ++) {
          if (s.charAt(i!= cr += s.charAt(i);
          }
       return r;
    }

}


Вообще, меня терзают сомнения по поводу правильности реализации массива с потоками. Думается, что в Java есть что-то специальное для таких целей, но я реализовал первое что пришло в голову — массив потоков.
И не ругайте за изобретение велосипеда. Тут цель была just for fun плюс с MySQL'ем поработать. Для красоты вывода этого добра в консоль можно закоментировать все PrintStackTrace()'ы.

Результат

Кусок проверенной базы. -1 в latency означает дохлую проксю.

P.S.
Драйвер MySQL можно скачать здесь: www.mysql.com/products/connector
Выбрать там JDBC Driver for MySQL (Connector/J). Архив распаковать, выдрать оттуда файлик mysql-connector-java-5.1.10-bin.jar и закинуть его в папку с проектом. Потом в Eclipse правой кнопкой по проекту -> Properties -> Java Build Path -> Libraries -> Add JARs и подцепить его там.

Вот то, что должно получится.

P.P.S
Экспорт кода из Eclipse в HTML реализован с помощью Java2Html converter

© @nixan
Tags:
Hubs:
Total votes 9: ↑3 and ↓6-3
Comments23

Articles