Created
June 27, 2017 16:59
-
-
Save snakers4/095bb9a43d3165b15b249ce9d77e9006 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<p></p><p><img src="https://pics.spark-in.me/upload/7b09411ab7b91bc8da7e79c763311795.jpg" style="width: 700px;"></p><p><span style="font-style: italic;">Как всегда надо начать с заманчивой картинки, но не пояснять ее смысл!</span></p><hr><p></p><p style="text-align: justify; ">Пару дней назад ко мне обратился далекий знакомый, мол у нас политесы в компании и мы не можем никак выбрать направление развития. Ситуация типичная - <span style="font-weight: bold;">лебедь, рак и щука</span> и надо бы очень быстро <span style="font-weight: bold;">проанализировать сайты его конкурентов</span>, вот держи картинку со списком самых крупных компаний в этой сфере. Оказалось, что конкуренты из сферы форекса. Это меня смутило, но не остановило. Я посмотрел на их сайты (а, забегая вперед, некоторые из них просто огромны - миллионы страниц) и тут у меня родилась гениальная идея.</p><p style="text-align: justify; ">Я занимался ей пару суток почти без остановки и накопал относительно интересные результаты, которыми и хочу поделиться с вами.</p><hr><p style="text-align: justify; "><img src="https://pics.spark-in.me/upload/ee4923168f58286b1cb17594ef7bc34a.png" style="width: 1127px;"><br></p><p style="text-align: justify; "><span style="font-style: italic;">Картинка, которую мне прислали. Даже nutshell не нужен.</span></p><hr><p style="text-align: justify; "><span style="font-weight: bold;">После визита на пару сайтов, у меня в голове промелькнули такие мысли:</span></p><ol><li style="text-align: justify;">Парсинг (или "<span style="font-weight: bold;">скрепинг"</span>) сайтов в современном мире - это лучшее средство business intelligence. Ибо сейчас все выкладывают весь свой контент для индексации в интернет;</li><li style="text-align: justify;">Недавно я видел отличную и простую <a href="http://pbpython.com/web-scraping-mn-budget.html" target="_blank" style="font-weight: bold; text-decoration-line: underline;">статью</a>, про то, что мол парсинг это чуть ли ваша обязанность, если вы аналитик;</li><li style="text-align: justify;">У меня есть знакомый, которому один раз предлагали спарсить весь вконтакте за 300,000 рублей 1 раз. Так вот он говорил, что в одноразовом парсинге на питоне вообще нет ничего сложного. Потоковый парсинг сложнее, там надо иметь прокси, VPN и балансировщик нагрузки и очередь;</li><li style="text-align: justify;">Самые крупные сайты как правило имеют сайтмапы;</li><li style="text-align: justify;">По этой причине этот подход в принципе применим для любой отрасли, где присутствует много контента;</li><li style="text-align: justify;">Идея также расширяется до выборочного парсинга самих страниц, но это на порядок сложнее технически (оставим это более прошаренным коллегам);</li></ol><p style="text-align: justify; "><br></p><p style="text-align: justify; ">Эта статья будет скорее оформлена в виде пошагового гайда, что немного в новинку для меня. Но почему нет, давайте попробуем?</p><h1 style="text-align: justify; "><span style="font-weight: bold;">1. Идея</span></h1><p style="text-align: justify; ">Через секунду после того, как я подумал про сайтмапы, у меня сразу родился план действий:</p><ul><li style="text-align: justify;">Найти все сайтмапы сайтов;</li><li style="text-align: justify;">Внести в массив, указав рекурсивные ли они;</li><li style="text-align: justify;">Собрать итоговый список сайтмапов (а их явно будут сотни или тысячи);</li><li style="text-align: justify;">Спарсить их;</li><li style="text-align: justify;">Распарсить урлы на составляющие;</li><li style="text-align: justify;">Сделать семантический анализ составляющий урлов;</li><li style="text-align: justify;">Если хватит сил, прогнать bag-of-words анализ, снизить размерность через метод главных компонент (PCA) и посмотреть что будет;</li><li style="text-align: justify;">Построить визуализации для самых популярных слов;</li><li style="text-align: justify;">Сделать простейшие сводные таблицы;</li><li style="text-align: justify;">Если будет нужно и полезно - подключить к процессу еще и словесные вектора (word2vec);</li></ul><p style="text-align: justify;"><br></p><p style="text-align: justify;">Забегая вперед, что получилось посмотреть можно тут:</p><ul><li style="text-align: justify;"><a href="https://goo.gl/doBG53" target="_blank" style="font-weight: bold; text-decoration-line: underline;">ipynb</a>;</li><li style="text-align: justify;"><a href="https://resources.spark-in.me/sitemap-analysis.html" target="_blank" style="font-weight: bold; text-decoration-line: underline;">HTML</a>;</li></ul><p style="text-align: justify;">Проще всего вам будет следить за повествованием установив себе зависимости, запустив jupyter notebook и выполняя код последовательно. Внимание (!) - иногда из-за размерности файлов отжирается вся память (на моей песочнице 16ГБ) - будьте внимательны! Для простейшего мониторинга рекомендую <a href="https://nicolargo.github.io/glances/" target="_blank" style="font-weight: bold; text-decoration-line: underline;">glances</a>.<span style="font-weight: bold; font-size: 25px; letter-spacing: -0.015em;"><br></span></p><p style="text-align: justify;"><span style="font-weight: bold; font-size: 25px; letter-spacing: -0.015em;">2. Зависимости (Common libraries, Code progress utility)</span></p><p style="text-align: justify;">Все делается на третьем питоне. </p><ul><li style="text-align: justify;">По сути вам нужен сервер с python3 и <a href="http://jupyter.org" target="_blank" style="font-weight: bold; text-decoration-line: underline;">jupyter notebook</a> (без этого будет гораздо дольше). </li><li style="text-align: justify;">Также я очень советую поставить себе <a href="https://t.me/snakers4/809" target="_blank" style="font-weight: bold; text-decoration-line: underline;">плагин</a> <span style="font-weight: bold;">сollapsable / expandable jupyter cells;</span></li><li style="text-align: justify;">Список основных библиотек и зависимостей указан ниже (или я буду добавлять их в отдельных ячейках);</li></ul><p style="text-align: justify;"><br></p><pre style="text-align: justify;">from __future__ import print_function<br>import os.path<br>from collections import defaultdict<br>import string<br>import requests<br>import pandas as pd<br>import numpy as np<br>import matplotlib.pyplot as plt<br>import random<br>from sklearn.feature_extraction.text import CountVectorizer<br>import wordcloud<br>%matplotlib inline</pre><p style="text-align: justify; "><br></p><p style="text-align: justify; ">Если у вас все работает, то .ipynb файл откроется примерно так (сверните ячейки, если они не свернуты по умолчанию):</p><img src="https://pics.spark-in.me/upload/e794e45912f5da326a1e880b3277e836.png" style="width: 1153px;"><p style="text-align: justify; "><br></p><p style="text-align: justify; ">Также <a href="https://github.com/alexanderkuk/log-progress" target="_blank" style="font-weight: bold; text-decoration-line: underline;">отсюда</a> я взял небольшую утилитку для демонстрации прогресса парсинга. Инструкции по установке также по ссылке.</p><p style="text-align: justify; "><img src="https://pics.spark-in.me/upload/68747470733a2f2f686162726173746f726167652.gif" style="width: 446px;"><br></p><p style="text-align: justify; "><span style="font-size: 25px; font-weight: bold; letter-spacing: -0.375px;">3. Список сайтмапов (Sitemap list)</span><br></p><p style="text-align: justify; ">Тут все банально, гуляем по сайтам, ищем сайтмапы (обычно они лежат в корне с названием sitemap.xml). Поиск google по сайту также помогает. Записываем в лист словарей.</p><pre style="text-align: justify; ">sitemap_list = [ <br> {'url': 'https://www.ig.com/sitemap.xml', 'recursive': 1},<br> {'url': 'https://www.home.saxo/sitemap.xml', 'recursive': 0}, <br> {'url': 'https://www.fxcm.com/sitemap.xml', 'recursive': 1}, <br> {'url': 'https://www.icmarkets.com/sitemap_index.xml', 'recursive': 1}, <br> {'url': 'https://www.cmcmarkets.com/en/sitemap.xml', 'recursive': 0},<br> {'url': 'https://www.oanda.com/sitemap.xml', 'recursive': 0}, <br> {'url': 'http://www.fxpro.co.uk/en_sitemap.xml', 'recursive': 0}, <br> {'url': 'https://en.swissquote.com/sitemap.xml', 'recursive': 0}, <br> {'url': 'https://admiralmarkets.com/sitemap.xml', 'recursive': 0}, <br> {'url': 'https://www.xtb.com/sitemap.xml', 'recursive': 1}, <br> {'url': 'https://www.ufx.com/en-GB/sitemap.xml', 'recursive': 0}, <br> {'url': 'https://www.markets.com/sitemap.xml', 'recursive': 0}, <br> {'url': 'https://www.fxclub.org/sitemap.xml', 'recursive': 1}, <br> {'url': 'https://www.teletrade.eu/sitemap.xml', 'recursive': 1}, <br> {'url': 'https://bmfn.com/sitemap.xml', 'recursive': 0}, <br> {'url': 'https://www.thinkmarkets.com/en/sitemap.xml', 'recursive': 0}, <br> {'url': 'https://www.etoro.com/sitemap.xml', 'recursive': 1}, <br> {'url': 'https://www.activtrades.com/en/sitemap_index.xml', 'recursive': 1}, <br> {'url': 'http://www.fxprimus.com/sitemap.xml', 'recursive': 0}<br>]</pre><p style="text-align: justify; "><br></p><p style="text-align: justify; ">Тут немаловажно, что часть сайтмапов рекурсивная (то есть содержит ссылки на другие сайтмапы), а часть нет.</p><p style="text-align: justify; "><span style="font-size: 25px; font-weight: bold; letter-spacing: -0.375px;">4. Собственно сам сбор сайтмапов (Web scraping)</span></p><p style="text-align: justify; ">Поскольку часть админов указанных выше сайтов проверяют кто забирает сайтмап, можно попробовать на всякий случай притвориться юзером (можно гугл-ботом, но юзером полезнее научиться притворяться =) ). Все сайтмапы по ссылке выше открывались у меня в браузере. </p><p style="text-align: justify; ">Для этого нам поможет библиотека <span style="background-color: rgb(252, 252, 252); font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 12px; font-weight: bold;">fake_useragent:</span></p><pre style="text-align: justify; ">from fake_useragent import UserAgent<br>ua = UserAgent()<br>headers = ua.chrome<br>headers = {'User-Agent': headers}</pre><p style="text-align: justify; "><br></p><p style="text-align: justify; ">Если мы попробуем забрать один сайтмап, то мы увидим, что его надо декодировать</p><pre style="text-align: justify; ">result = requests.get(sitemap_list[3]['url'])<br>c = result.content<br>c = c.decode("utf-8-sig")<br>c</pre><p style="text-align: justify; "></p><p style="text-align: justify; ">Ответ выглядит примерно так:</p><blockquote style="text-align: justify; ">'<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="//www.icmarkets.com/main-sitemap.xsl"?>\n<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n\t<sitemap>\n\t\t<loc>https://www.icmarkets.com/post-sitemap.xml</loc>\n\t\t<lastmod>2016-12-16T07:13:32-01:00</lastmod>\n\t</sitemap>\n\t<sitemap>\n\t\t<loc>https://www.icmarkets.com/page-sitemap.xml</loc>\n\t\t<lastmod>2017-06-20T07:11:01+00:00</lastmod>\n\t</sitemap>\n\t<sitemap>\n\t\t<loc>https://www.icmarkets.com/attachment-sitemap1.xml</loc>\n\t\t<lastmod>2014-07-01T15:44:46+00:00</lastmod>\n\t</sitemap>\n\t<sitemap>\n\t\t<loc>https://www.icmarkets.com/attachment-sitemap2.xml</loc>\n\t\t<lastmod>2014-10-29T02:36:07-01:00</lastmod>\n\t</sitemap>\n\t<sitemap>\n\t\t<loc>https://www.icmarkets.com/attachment-sitemap3.xml</loc>\n\t\t<lastmod>2015-03-15T18:41:51-01:00</lastmod>\n\t</sitemap>\n\t<sitemap>\n\t\t<loc>https://www.icmarkets.com/attachment-sitemap4.xml</loc>\n\t\t<lastmod>2017-05-30T12:33:34+00:00</lastmod>\n\t</sitemap>\n\t<sitemap>\n\t\t<loc>https://www.icmarkets.com/category-sitemap.xml</loc>\n\t\t<lastmod>2016-12-16T07:13:32-01:00</lastmod>\n\t</sitemap>\n\t<sitemap>\n\t\t<loc>https://www.icmarkets.com/post_tag-sitemap.xml</loc>\n\t\t<lastmod>2014-03-27T01:14:54-01:00</lastmod>\n\t</sitemap>\n\t<sitemap>\n\t\t<loc>https://www.icmarkets.com/csscategory-sitemap.xml</loc>\n\t\t<lastmod>2013-06-11T00:02:10+00:00</lastmod>\n\t</sitemap>\n\t<sitemap>\n\t\t<loc>https://www.icmarkets.com/author-sitemap.xml</loc>\n\t\t<lastmod>2017-05-05T06:44:19+00:00</lastmod>\n\t</sitemap>\n</sitemapindex>\n<!-- XML Sitemap generated by Yoast SEO -->'</blockquote><p style="text-align: justify; "><br></p><p style="text-align: justify; ">Эта функция найденная на просторах интернета поможет нам декодировать XML дерево, которым является сайтмап:<br></p><pre style="text-align: justify; "># xml tree parsing<br>import xml.etree.ElementTree as ET<br><br>def xml2df(xml_data):<br> root = ET.XML(xml_data) # element tree<br> all_records = []<br> for i, child in enumerate(root):<br> record = {}<br> for subchild in child:<br> record[subchild.tag] = subchild.text<br> all_records.append(record)<br> return pd.DataFrame(all_records)</pre><p style="text-align: justify; "><br></p><p style="text-align: justify; ">Далее эта функция поможет нам собрать все сайтмапы в одном листе:</p><pre style="text-align: justify; ">end_sitemap_list = []<br>for sitemap in log_progress(sitemap_list, every=1):<br> if(sitemap['recursive']==1):<br> try:<br> result = requests.get(sitemap['url'], headers=headers)<br> c = result.content<br> c = c.decode("utf-8-sig")<br> df = xml2df(c)<br> end_sitemap_list.extend(list(df['{http://www.sitemaps.org/schemas/sitemap/0.9}loc'].values))<br> except:<br> print(sitemap)<br> else:<br> end_sitemap_list.extend([sitemap['url']])</pre><p style="text-align: justify; "><br></p><p style="text-align: justify; ">В разное время у меня получалось от 200 до 250 сайтмапов.</p><p style="text-align: justify; ">В итоге эта функция поможет нам собственно собрать данные сайтмапов и сохранить их в датафрейм pandas.</p><p style="text-align: justify; "><br></p><pre style="text-align: justify; ">result_df = pd.DataFrame(columns=['changefreq','loc','priority'])<br>for sitemap in log_progress(end_sitemap_list, every=1):<br> <br> result = requests.get(sitemap, headers=headers)<br> c = result.content<br> try:<br> c = c.decode("utf-8-sig")<br> df = xml2df(c)<br> columns = [<br> '{http://www.sitemaps.org/schemas/sitemap/0.9}changefreq',<br> '{http://www.sitemaps.org/schemas/sitemap/0.9}loc',<br> '{http://www.sitemaps.org/schemas/sitemap/0.9}priority'<br> ]<br> try: <br> df2 = df[columns]<br> df2['source'] = sitemap<br> df2.columns = ['changefreq','loc','priority','source']<br> except:<br> df2['loc'] = df['{http://www.sitemaps.org/schemas/sitemap/0.9}loc']<br> df2['changefreq'] = ''<br> df2['priority'] = ''<br> df2['source'] = sitemap<br> result_df = result_df.append(df2)<br> except:<br> print(sitemap)</pre><p style="text-align: justify; "><br></p><p style="text-align: justify; ">После нескольких минут ожидания у нас получается таблица размером (14047393, 4), что весьма неплохо для такого "наколеночного" решения! </p><p style="text-align: justify; ">Если вам понравился новый формат, пишите в личке, будем продолжать в таком же формате. Ну и эта статья - первая в цикле.</p><p style="text-align: justify; "><br></p><p style="text-align: justify; "><br></p><p style="text-align: justify; "><br></p> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment