Advertisement
viking_unet

Google companies parser

Jul 13th, 2020 (edited)
156
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 14.42 KB | None | 0 0
  1. from   urllib.parse import unquote
  2. import csv
  3. import logging
  4. import sys
  5. import traceback
  6. import re
  7. import locale
  8.  
  9. import bs4
  10. import requests
  11.  
  12.  
  13. contacts = [
  14.     "Контакты",
  15.     "контакты",
  16.     "О нас",
  17.     "о нас",
  18.     "О компании",
  19.     "о компании",
  20.     "Связаться с нами",
  21.     "связаться с нами",
  22.     ]
  23. contacts = [x for x in contacts if x]
  24.  
  25. ignor_emails = [
  26.     "web@coffeestudio.ru",
  27.     "Rating@Mail.ru",
  28.     "Рейтинг@Mail.ru",
  29.     "Ðåéòèíã@Mail.ru",
  30.     "",
  31.     ]
  32. ignor_emails = [x for x in ignor_emails if x]
  33.  
  34. # инициализация
  35. headers    = {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61",
  36.               "accept-language": "ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7"}
  37.  
  38. logging.basicConfig(datefmt="%Y-%m-%d %H:%M:%S",
  39.                     format="%(asctime)s: %(message)s",
  40.                     handlers=[logging.FileHandler('errors.log', 'a', 'utf-8')])
  41.  
  42. email_regexp = re.compile("""(>|[\s]+|"|')([^>\s="']+@[^<\s\.]+\.[^<\s=^"']+)([\s]+|<|"|')""")
  43.  
  44. locale_encoding = locale.getpreferredencoding()
  45.  
  46. # функция для приведения адреса к читаемому виду в случае с кавычками и html заквторированными символами (html entities)
  47. def repair_email(email):
  48.     if '\\' in email or '/' in email: return ''
  49.     res = re.findall('&#[\d]+;', email)
  50.     buf_email = email
  51.     for finder in res:
  52.         buf_email = buf_email.replace(finder, chr(int(finder[2:-1])))
  53.     if buf_email.startswith('"'): buf_email = buf_email[1:]
  54.     if buf_email.startswith("'"): buf_email = buf_email[1:]
  55.     if buf_email.endswith('"'):   buf_email = buf_email[:-1]
  56.     if buf_email.endswith("'"):   buf_email = buf_email[:-1]
  57.     if ':' in buf_email: buf_email = buf_email.split(':')[1]
  58.     if ';' in buf_email: buf_email = buf_email.split(';')[1]
  59.     #if buf_email.endswith(","):   buf_email = buf_email[:-1]
  60.     buf_email = buf_email.strip('.,')
  61.     if buf_email.endswith(".jpg") or buf_email.endswith(".png"): return ''
  62.     if buf_email.startswith('<!--'): buf_email = buf_email[4:]
  63.     if '@' not in buf_email: return ''
  64.     return buf_email.strip()
  65.  
  66. # поиск нужного тега со ссылкой на раздел контактов с адресами почты
  67. def has_text_contact(tag, contact):
  68.     if tag.name == 'a':
  69.         if contact in tag.text:
  70.             return tag
  71.         if tag.get('title', None) == contact:
  72.             return tag
  73.  
  74. # составление csv файла с перечнем организаций и их сайтов по запросу в google
  75. def parse_companies(query):
  76.    
  77.     try:
  78.         # инициализация
  79.         true_query = '+'.join(query.split())
  80.         companies  = []
  81.        
  82.         # выполняем основной запрос в Google
  83.         url  = "https://www.google.com/search?q=%s" % true_query
  84.         res  = requests.get(url, headers=headers)
  85.         soup = bs4.BeautifulSoup(res.text, "html.parser")
  86.         #with open("first_page.html", 'w', encoding = "utf-8") as f: f.write(soup.prettify())
  87.        
  88.         # ищем элемент с надписью "Другие места", а затем предыдущий тег <a> со ссылкой
  89.         places_span_tag = soup.find("span", text="Другие места")
  90.         next_page_url = None
  91.         for buf_tag in places_span_tag.parents:
  92.             if buf_tag.name == 'a':
  93.                 next_page_url = "https://www.google.com" + unquote(buf_tag["href"])
  94.                 break
  95.        
  96.         page_number = 1
  97.         # в цикле читаем все адреса и названия всех компаний на всех страницах
  98.         while next_page_url:
  99.            
  100.             print('page_number %s url "%s"' % (page_number, next_page_url))
  101.             page_number += 1
  102.            
  103.             # выполняем чтение новой страницы
  104.             res  = requests.get(next_page_url, headers=headers)
  105.             soup = bs4.BeautifulSoup(res.text, "html.parser")
  106.             #with open("next_page.html", 'w', encoding = "utf-8") as f: f.write(soup.prettify())
  107.                
  108.             # ищем все теги сайтов и по каждому из них находим имя организации
  109.             for site_div in soup.find_all("div", text="Сайт"):
  110.                 for buf_tag in site_div.parents:
  111.                     if buf_tag.name == 'a':
  112.                         site_url      = unquote(buf_tag["href"])
  113.                         buf_element   = buf_tag.previous_sibling # нахрдим предыдущий тег <a>
  114.                         title_element = buf_element.find("div", role="heading") # внутри тега <div> с именем
  115.                         company_name  = title_element.text.strip()
  116.                         if not [True for buf_name, buf_url in companies if buf_url.replace('www.', '') == site_url.replace('www.', '')] and \
  117.                            not [True for buf_name, buf_url in companies if company_name == buf_name]:
  118.                             if company_name.startswith('"') and company_name.endswith('"'):
  119.                                 company_name = company_name[1:-1]
  120.                             elif company_name.startswith("'") and company_name.endswith("'"):
  121.                                 company_name = company_name[1:-1]
  122.                             company_name = company_name.replace('«', '"').replace('»', '"')
  123.                             company_name = company_name.replace('""', '"').strip()
  124.                             companies.append([company_name, site_url])
  125.                         break
  126.            
  127.             # ищем следующую страницу, если на есть
  128.             next_page = soup.find("span", text="Следующая")
  129.             if next_page:
  130.                 for buf_tag in next_page.parents:
  131.                     if buf_tag.name == 'a':
  132.                         next_page_url = "https://www.google.com" + unquote(buf_tag["href"])
  133.                         break
  134.             else:
  135.                 next_page_url = None
  136.             #break
  137.  
  138.         # сортируем компании в лексикографическом порядке
  139.         companies.sort()
  140.        
  141.         #for name, site in companies: print(name, site)
  142.         #print('found %s comanies' % len(companies))
  143.        
  144.         if not companies:
  145.             print('sorry, but there is no companies found for query "%s"' % query)
  146.         else:
  147.             # пишем в csv файл найденные данные по компаниям
  148.             companies.insert(0, ["title", "site"])
  149.             with open("%s.csv" % query, 'w', newline='\n', encoding=locale_encoding) as csv_file:
  150.                 writer = csv.writer(csv_file, delimiter=';', quoting=csv.QUOTE_NONE, quotechar='')
  151.                 for line in companies:
  152.                     writer.writerow(line)
  153.             print("csv saved (%s companies)\n" % len(companies))
  154.             return True
  155.            
  156.     except Exception as err:
  157.         msg = "error in parse_companies: %s" % str(err)
  158.         print(msg)
  159.         logging.exception(msg)
  160.  
  161.  
  162. # ищем e-mail адреса на сайтах организаций на всех предполагаемых страницах контактов        
  163. def parse_contacts(query):
  164.    
  165.     # для тестовой отладки
  166.     igonre_tiltes = [
  167.         ]
  168.    
  169.     try:
  170.        
  171.         all_emails = []
  172.         empties    = []
  173.         companies  = []
  174.        
  175.         # открываем полученный ранее пропарсенный список организаций и их сайтов
  176.         with open(query+".csv", 'r', encoding=locale_encoding) as f:
  177.             data = [row[:-1].split(';') for row in f.readlines()]
  178.             data.pop(0)
  179.         # ищем почтовые адреса для всех компаний по очереди  
  180.         for number, (name, site) in enumerate(data):
  181.             #if number < N: continue
  182.             if   site.endswith('/en/'): site = site[:-4] + '/ru/'
  183.             elif site.endswith('/en'):  site = site[:-3] + '/ru'
  184.             print('%s/%s name="%s" site="%s"' % (number+1, len(data), name, site))
  185.             emails = set()
  186.             # получаем главную страницу сайта компании
  187.             try:
  188.                 res  = requests.get(site, headers=headers)
  189.                 soup = bs4.BeautifulSoup(res.text, "html.parser")
  190.             except Exception as err:
  191.                 msg = 'error in parse_contacts main site "%s": %s' % (site, str(err))
  192.                 print('empty')
  193.                 logging.error(msg)
  194.                 continue
  195.             # получаем все адреса почты на главной странице
  196.             buf_emails = email_regexp.findall(res.text)
  197.             buf_emails = [x[1] for x in buf_emails]
  198.             emails.update(set(buf_emails))
  199.             # получаем все почтовые адреса со всех найденных гипотетических страниц контактов
  200.             buf_hrefs = []
  201.             for contact_title in contacts:
  202.                 # ищем тег со ссылкой на страницу конактов
  203.                 find_a_tag = soup.find(lambda tag: has_text_contact(tag, contact_title))
  204.                 if find_a_tag:
  205.                     try:
  206.                         buf_href = find_a_tag['href']
  207.                     except:
  208.                         msg = 'error in parse_contacts - failed to find href site "%s", contact_title "%s", tag %s' % (site, contact_title, find_a_tag)
  209.                         #print('contact pass')
  210.                         logging.error(msg)
  211.                         continue
  212.                     # строим абсолютную ссылку на страницу контактов
  213.                     if buf_href.startswith('//'):
  214.                         buf_href = 'https:' + buf_href
  215.                     elif not buf_href.startswith('http'):
  216.                         if not buf_href.startswith('/'):
  217.                             buf_href = '/' + buf_href
  218.                         if site.endswith('/'):
  219.                             buf_href = site[:-1] + buf_href
  220.                         else:                  
  221.                             buf_href = site + buf_href
  222.                     # запрашиваем страницу с контактами
  223.                     buf_hrefs.append([contact_title, buf_href])
  224.                     try:
  225.                         res = requests.get(buf_href, headers=headers)
  226.                         #with open("temp_contact_%s.html" % contact_title, 'w', encoding = "utf-8") as f: f.write(res.text)
  227.                     except Exception as err:
  228.                         msg = 'error in parse_contacts - failed to request contact_url "%s" for site "%s": %s' % (buf_href, site, str(err))
  229.                         #print('contact pass')
  230.                         logging.error(msg)
  231.                         #print(msg)
  232.                         continue
  233.                     buf_emails = email_regexp.findall(res.text)
  234.                     buf_emails = [x[1] for x in buf_emails]
  235.                     emails.update(set(buf_emails))
  236.                     #print('find contact url:', contact_title, buf_href, buf_emails)
  237.             emails = [repair_email(email) for email in emails]
  238.             emails = [email for email in emails if email and email not in ignor_emails]
  239.             emails = sorted(list(set(emails)))
  240.             #all_emails.extend(emails)
  241.             #print('site emails:', emails)
  242.             if not emails:
  243.                 print('empty')
  244.  
  245.             # запоминаем компании в список с адресами и в список пустых адресов
  246.             if not emails:
  247.                 empties.append([name.replace('""', '"'), site])
  248.             else:
  249.                 companies.append([name.replace('""', '"'), site, ', '.join(emails)])
  250.                
  251.             """
  252.            # для отладки
  253.            if not emails:
  254.                print('no emails')
  255.                # если не было найден никакой страницы со ссылками, то вывести в файл страницу
  256.                if not buf_hrefs:
  257.                    with open("temp.html", 'w', encoding = "utf-8") as f:
  258.                        f.write(soup.prettify())
  259.                else:
  260.                    for buf_row in buf_hrefs:
  261.                        print(buf_row)
  262.  
  263.                if name in igonre_tiltes: continue
  264.                break
  265.            """
  266.        
  267.         # пишем в csv файл найденные данные по компаниям
  268.         companies.insert(0, ["title", "site", "emails"])
  269.         with open("%s (emails).csv" % query, 'w', newline='\n', encoding=locale_encoding) as csv_file:
  270.             for line in companies:
  271.                 if ';' in line:
  272.                     print('!', line)
  273.                 csv_file.write(';'.join(line) + '\n')
  274.         print("csv saved (%s companies)\n" % (len(companies)-1))
  275.        
  276.         # выводим список компаний без найденных почтовых адресов
  277.         print('empties: %s' % len(empties))
  278.         for number, (name, site) in enumerate(empties):
  279.             print('%s/%s name="%s" site="%s"' % (number+1, len(empties), name, site))
  280.        
  281.         # вывод результатов финальной статистики
  282.         print('\nresult statistic: produced contacts %s, empty contacts %s' % (len(companies)-1, len(empties)))    
  283.        
  284.         # для отладки
  285.         #all_emails.sort()
  286.         #for email in all_emails:
  287.         #    print(email)
  288.        
  289.     except Exception as err:
  290.         msg = "error in parse_companies: %s" % str(err)
  291.         print(msg)
  292.         logging.exception(msg)
  293.  
  294. if __name__ == '__main__':
  295.    
  296.     query = input('Введите запрос для google поиска: ').strip()
  297.     #query = 'Заводы Санкт-Петербург'
  298.    
  299.     assert query, 'Пустой запрос'
  300.    
  301.     res = parse_companies(query)
  302.     if res:
  303.         parse_contacts(query)
  304.    
  305.     print("\ndone")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement