-Поиск по дневнику

Поиск сообщений в rss_rss_hh_new

 -Подписка по e-mail

 

 -Статистика

Статистика LiveInternet.ru: показано количество хитов и посетителей
Создан: 17.03.2011
Записей:
Комментариев:
Написано: 51


Быстрый анализ сайтов конкурентов через сайтмапы. Часть 1 — парсинг

Понедельник, 26 Июня 2017 г. 06:56 + в цитатник
image
Как всегда надо начать с заманчивой картинки, но не пояснять ее смысл!

Пару дней назад ко мне обратился далекий знакомый, мол у нас политесы в компании и мы не можем никак выбрать направление развития. Ситуация типичная — лебедь, рак и щука и надо бы очень быстро проанализировать сайты его конкурентов, вот держи картинку со списком самых крупных компаний в этой сфере. Оказалось, что конкуренты из сферы финансов. Это меня смутило, но не остановило. Я посмотрел на их сайты (а, забегая вперед, некоторые из них просто огромны — миллионы страниц) и тут у меня родилась гениальная идея.

Я занимался ей пару суток почти без остановки и накопал относительно интересные результаты, которыми и хочу поделиться с вами.


image
Картинка, которую мне прислали. Даже nutshell не нужен.

После визита на пару сайтов, у меня в голове промелькнули такие мысли:
  1. Парсинг (или «скрепинг») сайтов в современном мире — это лучшее средство business intelligence. Ибо сейчас все выкладывают весь свой контент для индексации в интернет;
  2. Недавно я видел отличную и простую статью, про то, что мол парсинг это чуть ли ваша обязанность, если вы аналитик;
  3. У меня есть знакомый, которому один раз предлагали спарсить весь вконтакте за 300,000 рублей 1 раз. Так вот он говорил, что в одноразовом парсинге на питоне вообще нет ничего сложного. Потоковый парсинг сложнее, там надо иметь прокси, VPN и балансировщик нагрузки и очередь;
  4. Самые крупные сайты как правило имеют сайтмапы;
  5. По этой причине этот подход в принципе применим для любой отрасли, где присутствует много контента;
  6. Идея также расширяется до выборочного парсинга самих страниц, но это на порядок сложнее технически (оставим это более прошаренным коллегам);


Эта статья будет скорее оформлена в виде пошагового гайда, что немного в новинку для меня. Но почему нет, давайте попробуем?

1. Идея



Через секунду после того, как я подумал про сайтмапы, у меня сразу родился план действий:
  • Найти все сайтмапы сайтов;
  • Внести в массив, указав рекурсивные ли они;
  • Собрать итоговый список сайтмапов (а их явно будут сотни или тысячи);
  • Спарсить их;
  • Распарсить урлы на составляющие;
  • Сделать семантический анализ составляющий урлов;
  • Если хватит сил, прогнать bag-of-words анализ, снизить размерность через метод главных компонент (PCA) и посмотреть что будет;
  • Построить визуализации для самых популярных слов;
  • Сделать простейшие сводные таблицы;
  • Если будет нужно и полезно — подключить к процессу еще и словесные вектора (word2vec);


Забегая вперед, что получилось посмотреть можно тут:


Проще всего вам будет следить за повествованием установив себе зависимости, запустив jupyter notebook и выполняя код последовательно. Внимание (!) — иногда из-за размерности файлов отжирается вся память (на моей песочнице 16ГБ) — будьте внимательны! Для простейшего мониторинга рекомендую glances (ну или просто делайте все на 10% датасета).

2. Зависимости (Common libraries, Code progress utility)



Все делается на третьем питоне.

  • По сути вам нужен сервер с python3 и jupyter notebook (без этого будет гораздо дольше).
  • Также я очень советую поставить себе плагин сollapsable / expandable jupyter cells;
  • Список основных библиотек и зависимостей указан ниже (или я буду добавлять их в отдельных ячейках);


from __future__ import print_function
import os.path
from collections import defaultdict
import string
import requests
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import random
from sklearn.feature_extraction.text import CountVectorizer
import wordcloud
%matplotlib inline


Если у вас все работает, то .ipynb файл откроется примерно так (сверните ячейки, если они не свернуты по умолчанию):
image

Также отсюда я взял небольшую утилитку для демонстрации прогресса парсинга. Инструкции по установке также по ссылке.
image

3. Список сайтмапов (Sitemap list)


Тут все банально, гуляем по сайтам, ищем сайтмапы (обычно они лежат в корне с названием sitemap.xml). Поиск google по сайту также помогает. Записываем в лист словарей.

sitemap_list = [ 
    {'url': 'https://www.ig.com/sitemap.xml', 'recursive': 1},
    {'url': 'https://www.home.saxo/sitemap.xml', 'recursive': 0},    
    {'url': 'https://www.fxcm.com/sitemap.xml', 'recursive': 1},  
    {'url': 'https://www.icmarkets.com/sitemap_index.xml', 'recursive': 1},  
    {'url': 'https://www.cmcmarkets.com/en/sitemap.xml', 'recursive': 0},
    {'url': 'https://www.oanda.com/sitemap.xml', 'recursive': 0},     
    {'url': 'http://www.fxpro.co.uk/en_sitemap.xml', 'recursive': 0}, 
    {'url': 'https://en.swissquote.com/sitemap.xml', 'recursive': 0}, 
    {'url': 'https://admiralmarkets.com/sitemap.xml', 'recursive': 0},     
    {'url': 'https://www.xtb.com/sitemap.xml', 'recursive': 1},       
    {'url': 'https://www.ufx.com/en-GB/sitemap.xml', 'recursive': 0},   
    {'url': 'https://www.markets.com/sitemap.xml', 'recursive': 0},   
    {'url': 'https://www.fxclub.org/sitemap.xml', 'recursive': 1},       
    {'url': 'https://www.teletrade.eu/sitemap.xml', 'recursive': 1},       
    {'url': 'https://bmfn.com/sitemap.xml', 'recursive': 0},       
    {'url': 'https://www.thinkmarkets.com/en/sitemap.xml', 'recursive': 0},  
    {'url': 'https://www.etoro.com/sitemap.xml', 'recursive': 1},  
    {'url': 'https://www.activtrades.com/en/sitemap_index.xml', 'recursive': 1},  
    {'url': 'http://www.fxprimus.com/sitemap.xml', 'recursive': 0}
]


Тут немаловажно, что часть сайтмапов рекурсивная (то есть содержит ссылки на другие сайтмапы), а часть нет.

4. Собственно сам сбор сайтмапов (Web scraping)



Поскольку часть админов указанных выше сайтов проверяют кто забирает сайтмап, можно попробовать на всякий случай притвориться юзером (можно гугл-ботом, но юзером полезнее научиться притворяться =) ). Все сайтмапы по ссылке выше открывались у меня в браузере.

Для этого нам поможет библиотека fake_useragent:

from fake_useragent import UserAgent
ua = UserAgent()
headers = ua.chrome
headers = {'User-Agent': headers}


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


result = requests.get(sitemap_list[3]['url'])
c = result.content
c = c.decode("utf-8-sig")
c


Ответ выглядит примерно так (это сайтмап сайтмапов):

'\n\n\t\n\t\thttps://www.icmarkets.com/post-sitemap.xml\n\t\t2016-12-16T07:13:32-01:00\n\t\n\t\n\t\thttps://www.icmarkets.com/page-sitemap.xml\n\t\t2017-06-20T07:11:01+00:00\n\t\n\t\n\t\thttps://www.icmarkets.com/attachment-sitemap1.xml\n\t\t2014-07-01T15:44:46+00:00\n\t\n\t\n\t\thttps://www.icmarkets.com/attachment-sitemap2.xml\n\t\t2014-10-29T02:36:07-01:00\n\t\n\t\n\t\thttps://www.icmarkets.com/attachment-sitemap3.xml\n\t\t2015-03-15T18:41:51-01:00\n\t\n\t\n\t\thttps://www.icmarkets.com/attachment-sitemap4.xml\n\t\t2017-05-30T12:33:34+00:00\n\t\n\t\n\t\thttps://www.icmarkets.com/category-sitemap.xml\n\t\t2016-12-16T07:13:32-01:00\n\t\n\t\n\t\thttps://www.icmarkets.com/post_tag-sitemap.xml\n\t\t2014-03-27T01:14:54-01:00\n\t\n\t\n\t\thttps://www.icmarkets.com/csscategory-sitemap.xml\n\t\t2013-06-11T00:02:10+00:00\n\t\n\t\n\t\thttps://www.icmarkets.com/author-sitemap.xml\n\t\t2017-05-05T06:44:19+00:00\n\t\n\n'


Эта функция найденная на просторах интернета поможет нам декодировать XML дерево, которым является сайтмап:

# xml tree parsing
import xml.etree.ElementTree as ET

def xml2df(xml_data):
    root = ET.XML(xml_data) # element tree
    all_records = []
    for i, child in enumerate(root):
        record = {}
        for subchild in child:
            record[subchild.tag] = subchild.text
            all_records.append(record)
    return pd.DataFrame(all_records)


Далее эта функция поможет нам собрать все сайтмапы в одном листе:

end_sitemap_list = []
for sitemap in log_progress(sitemap_list, every=1):
    if(sitemap['recursive']==1):
        try:
            result = requests.get(sitemap['url'], headers=headers)
            c = result.content
            c = c.decode("utf-8-sig")
            df = xml2df(c)
            end_sitemap_list.extend(list(df['{http://www.sitemaps.org/schemas/sitemap/0.9}loc'].values))
        except:
            print(sitemap)
    else:
        end_sitemap_list.extend([sitemap['url']])

В разное время у меня получалось от 200 до 250 сайтмапов.

В итоге эта функция поможет нам собственно собрать данные сайтмапов и сохранить их в датафрейм pandas.

result_df = pd.DataFrame(columns=['changefreq','loc','priority'])
for sitemap in log_progress(end_sitemap_list, every=1):
    
    result = requests.get(sitemap, headers=headers)
    c = result.content
    try:
        c = c.decode("utf-8-sig")
        df = xml2df(c)
        columns = [
            '{http://www.sitemaps.org/schemas/sitemap/0.9}changefreq',
            '{http://www.sitemaps.org/schemas/sitemap/0.9}loc',
            '{http://www.sitemaps.org/schemas/sitemap/0.9}priority'
        ]
        try: 
            df2 = df[columns]
            df2['source'] = sitemap
            df2.columns = ['changefreq','loc','priority','source']
        except:
            df2['loc'] = df['{http://www.sitemaps.org/schemas/sitemap/0.9}loc']
            df2['changefreq'] = ''
            df2['priority'] = ''
            df2['source'] = sitemap
        result_df = result_df.append(df2)
    except:
        print(sitemap)

После нескольких минут ожидания у нас получается таблица размером (14047393, 4), что весьма неплохо для такого «наколеночного» решения!

Если вам понравился новый формат, пишите в личке, будем продолжать в таком же формате. Ну и эта статья — первая в цикле.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331572/


 

Добавить комментарий:
Текст комментария: смайлики

Проверка орфографии: (найти ошибки)

Прикрепить картинку:

 Переводить URL в ссылку
 Подписаться на комментарии
 Подписать картинку