Всем привет.

История эта случилась из-за экономии. При покупке платного аккаунта мы даже не обратили внимание на то, сколько полезного даёт тариф Эксперт.

Ах, если бы мы обратили на это внимание, то мне не пришлось бы осваивать Selenium и основы Java.

Как-то раз возникла маркетинговая задача — собрать контакты закупщиков в компаниях по нашей тематике.

Ищу все конкурсы, подходящие по ключевым словам и в нужных регионах. За все прошлые годы, завершенные и текущие.

В выгрузке на тарифе Стандарт вот такие данные:



Есть только название компании. Контактных данных закупщика нет.

Теперь, чтобы не собирать вручную контактные данные закупщика, принимаю решение писать скрипт.

Так как у Контур.Закупок вся страничка загружается javascript-ом, то простым парсингом тут не отделаешься. Придётся писать скрипт на Selenium.

Для начала подготовим исходные данные — сохраняем в CSV номер конкурса (он же параметр в URL), а также название компании, чтобы не парсить её. Всё это в файл kontur_getContacts_src.csv



Далее, скрипт на Java с комментариями

import org.openqa.selenium.By;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.*;
import org.openqa.selenium.firefox.*;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.Select;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.io.*;
import java.nio.file.*;


public class kontur_getContacts{

	public static void main(String args[]) throws Exception{
		//Запускаем коннектор к FireFox
		System.setProperty("webdriver.gecko.driver", "geckodriver.exe");
	
		
		FirefoxDriver driver = new FirefoxDriver();// Create a Firefox browser instance
		
		driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
		
		
		// Логинимся в Контур.Закупки		
		
		driver.get("https://auth.kontur.ru/login.aspx?authmode=certlogin&back=https%3A%2F%2Fzakupki.kontur.ru%2FLogin%2FCallback%3FReturnUrl%3Dhttps%253A%252F%252Fzakupki.kontur.ru%252F%253Fevent-login%253D1&customize=zakupki"); //Открываем страничку логина
		
		Thread.sleep(2*1000);
		
		WebElement login_field = driver.findElement(By.cssSelector(".loginPassword__login input")); // Ищем поле логин
		login_field.sendKeys("sales@mycomp.ru");//Посылаем текст элементу логин
		WebElement pass_field = driver.findElement(By.cssSelector("input.input__realInput")); //Ищем поле Пароль
		pass_field.sendKeys("password");//Посылаем текст элементу пароль
		Thread.sleep(1*1000); //Жду
		driver.findElement(By.cssSelector("input.button__input")).click(); // Ставлю галочку Оставаться в системе
		Thread.sleep(1*1000); //Жду
		driver.findElement(By.cssSelector(".loginPassword__submit .button__input")).click(); //Отправляю форму
		Thread.sleep(2*1000); //Жду
		
		String sourceFilePath = "C:/Users/user/selenium/kontur_getContacts_src.csv";//Файл, откуда брать исходную информацию
		String resultFilePath = "C:/Users/user/selenium/kontur_getContacts_result.csv";//Файл, куда писать результаты
		String delimeter = ";"; // Разделитель в файле с исходной информацией
		//Объявляем переменные
		String name; String text; 
		Integer flag_hasName=0;
		Integer flag_oneVar=0;
		
		try{
			//Откроем файл с проектами
			FileInputStream fstream = new FileInputStream(sourceFilePath);
			BufferedReader br = new BufferedReader(new InputStreamReader(fstream));
			String strLine;//Объявляем переменные
			String[] subStr;//Объявляем переменные
			//Очищаем результирующий файл
			byte[] myBytes = "".getBytes(); 
			Files.write(Paths.get(resultFilePath), myBytes);
		 
			
			while ((strLine = br.readLine()) != null){ //Читаем файл с исходными данными построчно
				System.out.println(strLine); //Смотрим в консоли текущуб строку
			  
			  	//Делим текущую строку по делимитеру	 
				subStr = strLine.split(delimeter);
				System.out.println("Found order_id "+subStr[0]+" and Organization is "+subStr[1]);
				//Открываем страничку с этой закупкой
				driver.get("https://zakupki.kontur.ru/"+subStr[0]);

				/* Находим первую ссылочку на контакт закупщика */
				name =""; //Очищаем переменную, в кот будет имя заказчика
				text =""; //Очищаем переменную, в кот будут результаты работы скрипта
				
				List<WebElement> tenderField_data_in=driver.findElements(By.cssSelector(".popup__mainContacts .tenderField__s div.tenderField_data_in")); //Находим первую ссылку
				if(tenderField_data_in.size()>0){ 
				
					
					WebElement man = driver.findElement(By.cssSelector(".popup__mainContacts .tenderField__s div.tenderField_data_in"));//Находим поле имя
				
					System.out.println("FOUND is "+man.getAttribute("textContent")); //Смотрим имя в консоли
					
					name = man.getAttribute("textContent").trim(); //Присваиваем имя переменной name
				
				}
				
				// Ищем ссылки с данными заказчика
				List<WebElement> elements = driver.findElements(By.cssSelector(".tenderField .tenderField_data_in a"));
				
				System.out.println("Count elements in array is "+elements.size()); //Смотрим в консоли количество найденных элементов данных
				
				if(elements.size()>0){ //Есть линки на странице
				
					WebElement email = driver.findElement(By.cssSelector(".tenderField .tenderField_data_in a")); //Выделяем емейл
				
					text=subStr[1]+delimeter+email.getAttribute("href")+delimeter+name+"\r\n"; // Формируем строку для записи в файл с результатами
						
					Files.write(Paths.get(resultFilePath), text.getBytes(), StandardOpenOption.APPEND); //Дописываем в файл

					
				} 
				
		   }
		}catch (IOException e){
		   System.out.println("Error opening or processing file with finished purchases");
		}
		
		// Close the browser instance
		driver.quit();

	}
}

Запускаем скрипт Selenium

C:\Users\user\selenium>"C:\Program Files\Java\jdk-10.0.1\bin\javac" -cp selenium-server-standalone-3.13.0.jar; kontur_getContacts.java
* C:\Users\user\selenium>"C:\Program Files\Java\jre-10.0.1\bin\java" -cp selenium-server-standalone-3.13.0.jar; kontur_getContacts


Результатом будет вот такой файл с контактами, который можно использовать в маркетинговых целях.

Комментарии (11)


  1. saw_tooth
    29.11.2018 15:30
    +1

    Thread.sleep(2*1000); //Жду

    image
    Есть же wait for!
    WebDriverWait wait = new WebDriverWait(webDriver, timeoutInSeconds);
    wait.until(ExpectedConditions.visibilityOfElementLocated(By.id<locator>));


    1. iTarakan Автор
      29.11.2018 15:33

      Это мой первый скрипт на Java. Я вообще то PHP-шник. А Java я гуглю и пишу. Соответственно, что первое попалось, то и пишу.


      1. saw_tooth
        29.11.2018 15:38

        Я вообще то PHP-шник.

        Понял.
        $driver->wait()->until(
            function () use ($driver) {
                $elements = $driver->findElements(WebDriverBy::cssSelector('li.foo'));
        
                return count($elements) > 5;
            },
            'Error locating more than five elements'
        );

        Заголовок спойлера
        Если Вы не поняли моего предыдущего сообщения, то я объясню: сначала нужно изучить инструмент, потом использовать, и только потом пилить на Хабр статью. Умолчу, что Вашу задачу можно было решить 1000 и 1 способом и совсем без selenium-а


        1. iTarakan Автор
          29.11.2018 16:10

          Решить то можно и без Seleniuma. Ну я решил вот так сделать, и, может быть, это кому то будет полезным готовым скриптом по работе с Контуром.

          А по поводу «Thread.sleep(2*1000);» — я руководствовался тем, что не знаю систему безопасности в Контуре, поэтому, чтобы не забанили аккаунт, притворялся простым юзером, который вводит данные с паузами. Паузу оно делает, поэтому задачу свою выполняет. Если что-то не нравится в написании, тут каждый может что-то добавить, что-то убрать.


      1. Light_Metal
        29.11.2018 15:44

  1. shirase55
    29.11.2018 15:32

    Зачем такие сложности. Посмотреть в devtools ajax запрос и повторить его. Если защиты нет то это 10 минут работы.


    1. iTarakan Автор
      29.11.2018 15:38

      1. Я не нашёл, где они в ajax-запросах. Может быть плохо искал, но…
      2. К тому же там ещё сложность в посылке логина системе. Страничка логина не простая, а защищённая. Вот из-за него и пришлось работать из браузера.


      1. saw_tooth
        29.11.2018 15:44

        Страничка логина не простая, а защищённая.

        Самая обычная

        Заголовок спойлера
        Использовать нужно request типа Session, который хранит всю мета-информацию, от запроса к запросу


        1. iTarakan Автор
          29.11.2018 15:58

          Ну да, вижу, что у Вас получилось отловить этот ajax.


          1. saw_tooth
            29.11.2018 16:08

            В dev-tool браузера, на вкладке Network, есть волшебная галочка Preserve Log.


            1. iTarakan Автор
              29.11.2018 16:12

              Я тоже теперь вижу этот запрос. Спасибо, полезно!