Настройка антисканирования Dianping.com создала нам много препятствий при сканировании веб-сайта Dianping. На странице мы видим это
На странице можно увидеть, что у этого ресторана 1405 отзывов, 387 на душу населения. Но когда мы анализируем исходный код страницы, мы не можем видеть цифры на странице, мы видим такой код
Dianping.com обработал цифры, и некоторая цифровая информация, такая как количество комментариев на душу населения и рейтинги, была защищена от сканирования. Количество комментариев на приведенной выше веб-странице составляет 1405, но в исходном коде страницы, кроме первого числа 1, мы не можем видеть следующие числа, которые все являются классами CSS, такими как случайное кодирование.
Если мы внимательно проанализируем этот класс CSS, нетрудно найти принцип, лежащий в его основе.
Через инструменты разработчика мы находим определение этого css, мы видим, что оно выглядит следующим образом
В свойстве background-image есть url, открываем его в браузере и видим, что его содержимое
Класс css lc-mY1i содержит свойство фона, которое определяет положение смещения фонового изображения.
Таким образом, принцип отображения чисел на веб-сайте комментариев заключается в отображении чисел в соответствующих позициях фоновых картинок путем установки различных смещенных позиций. Мы можем представить окно перед фоновым изображением, достаточно большое, чтобы отобразить число. Окно фиксируется, а фоновое изображение перемещается за ним.Перемещение в другую позицию может отображать номер в этой позиции.
Дальнейший анализ фонового изображения позволяет обнаружить, что это SVG-изображение, а числа на изображении можно увидеть в исходном коде svg следующим образом.
Поняв принцип, мы используем код для реализации процесса синтаксического анализа.
Во-первых, мы находим URL-адрес файла css на веб-странице с комментариями, код выглядит следующим образом.
def get_css():
url = "http://www.dianping.com/shanghai/ch10"
r = requests.get(url, headers=headers)
content = r.content.decode("utf-8")
matched = re.search(r'href="([^"]+svgtextcss[^"]+)"', content, re.M)
if not matched:
raise Exception("cannot find svgtextcss file")
css_url = matched.group(1)
css_url = fix_url(css_url)
return css_url
Затем мы находим путь к фоновому изображению из css и получаем каждое число в изображении SVG.
def get_svg(css_url):
r = requests.get(css_url, headers=headers)
content = r.content.decode("utf-8")
matched = re.search(r'span\[class\^="lc\-"\].*?background\-image: url\((.*?)\);', content)
if not matched:
raise Exception("cannot find svg file")
svg_url = matched.group(1)
svg_url = fix_url(svg_url)
r = requests.get(svg_url, headers=headers)
content = r.content.decode("utf-8")
matched = re.search(r'class="textStyle">(\d+)</text>', content)
if not matched:
raise Exception("cannot find digits")
digits = list(matched.group(1))
return digits
Эта функция возвращает массив, содержащий все числа в изображении SVG.
Для чисел, представленных классом css на веб-странице комментариев, давайте проанализируем соответствие между классом css и числами.
def get_class_offset(css_url):
r = requests.get(css_url, headers=headers)
content = r.content.decode("utf-8")
matched = re.findall(r'(\.[a-zA-Z0-9-]+)\{background:(\-\d+\.\d+)px', content)
result = {}
for item in matched:
css_class = item[0][1:]
offset = item[1]
result[css_class] = offset
return result
Эта функция возвращает словарь, ключом которого является имя класса css, а значением является смещение числа, соответствующего классу css на фоновом изображении.
Далее возьмем количество отзывов в качестве примера, чтобы получить количество отзывов для каждого ресторана на предыдущей странице обзора. Сначала определите функцию, чтобы получить количество комментариев
def get_review_num(page_url, class_offset, digits):
r = requests.get(page_url, headers=headers)
content = r.content.decode("utf-8")
root = etree.HTML(content)
shop_nodes = root.xpath('.//div[@id="shop-all-list"]/ul/li')
for shop_node in shop_nodes:
name_node = shop_node.xpath('.//div[@class="tit"]/a')[0]
name = name_node.attrib["title"]
review_num_node = shop_node.xpath('.//div[@class="comment"]/a[@class="review-num"]/b')[0]
num = 0
if review_num_node.text:
num = num * 10 + int(review_num_node.text)
for digit_node in review_num_node:
css_class = digit_node.attrib["class"]
offset = class_offset[css_class]
index = int((float(offset)+7)/-12)
digit = int(digits[index])
num = num * 10 + digit
last_digit = review_num_node[-1].tail
if last_digit:
num = num * 10 + int(last_digit)
print("restaurant: {}, review num: {}".format(name, num))
Затем вызовите функцию для сканирования количества отзывов для каждого ресторана на странице.
css_url = get_css()
digits = get_svg(css_url)
class_offset = get_class_offset(css_url)
url = "http://www.dianping.com/shanghai/ch10/g116"
get_review_num(url, class_offset, digits)
После запуска кода я получаю следующий результат
restaurant: 1886汽车主题德国餐厅(环宇荟店), review num: 1021
restaurant: Mia Fringe迷芬奇餐厅&酒吧, review num: 152
restaurant: Oyster EXPO江月蚝庭西餐生蚝吧(世博源店), review num: 1405
restaurant: 宝莱纳餐厅(陆家嘴店), review num: 7854
restaurant: Pizza Marzano玛尚诺(港汇店), review num: 7527
restaurant: love&salt牛排馆, review num: 86
restaurant: Da Ivo 意大利魔镜餐厅, review num: 3497
restaurant: Mr Nice好好先生餐厅(月星环球港店), review num: 9052
restaurant: L'ATELIER de Joël Robuchon, review num: 2821
restaurant: Stone Sal 言盐西餐厅, review num: 62
restaurant: 夏朵花园, review num: 3031
restaurant: 壳里西餐厅Coquille Seafood Bistro, review num: 322
restaurant: ICHA Chateau Bar & Restaurant(酒吧创意料理), review num: 496
restaurant: 菲斯特花园西餐厅, review num: 655
restaurant: 宝丽嘉酒店Cafe Bellagio(宝丽嘉西餐厅), review num: 598
Сравнивая данные на веб-странице, мы видим, что количество отзывов о ресторане анализируется корректно.
Эта статья была синхронно обновлена в общедоступной учетной записи [Python и анализ данных], пожалуйста, обратите внимание ~