用python做爬虫特别的简单:美团网数据采集技巧,有基础就开爬!
优采云 发布时间: 2020-08-09 12:041.数据采集工具介绍
现在的大多数动态网站,都是由浏览器端通过js发起ajax恳求,拿到数据后再渲染完成页面展示。这种情况下采集数据,通过脚本发起http的get恳求,拿到DOM文档页面后再解析提取有用数据的方式是行不通的。然后又有人会想到通过F12打开浏览器控制台剖析服务端api,再模拟恳求相应的api来领到我们想要的数据,这种思路在一些情况下可行,但是好多小型网站都会采取一些反爬策略,出于安全性考虑,往往对插口降低了安全验证,比如只有设置了相关的header和cookie,才能对页面进行恳求;还有的对恳求来源也做了限制等等,这个时侯通过这些方法采集数据就愈加困难了。我们还有其他有效的方式吗?当然,python做爬虫特别的简单,我们先来了解一下Selenium和Selectors,然后通过爬取美团网上店家信息的事例总结一下数据采集的一些方法:
对Python感兴趣或则是正在学习的小伙伴,可以加入我们的Python学习扣qun:784758214,看看前辈们是怎样学习的!从基础的python脚本到web开发、爬虫、django、数据挖掘等,零基础到项目实战的资料都有整理。送给每一位python的小伙伴!分享一些学习的方式和须要注意的小细节,教你怎么实现边学习边用Python挣钱的学习方法。点击加入我们的 python学习者集聚地
2.页面抓取数据剖析和数据表创建
以朝阳大悦城中的一家小吃店为例进行数据采集,网址是:
https://www.meituan.com/meishi/40453459/
2.1 抓取数据
我们要抓取的第一部分数据是店家的基本信息,包括店家名称、地址、电话、营业时间,分析多个美食类店家我们可知,这些店家的web界面在布局上基本是一致的,所以我们的爬虫可以写的比较通用。为了避免对店家数据的重复抓取,我们将店家的网址信息也储存到数据表中。
第二部份要抓取的数据是小吃店的招牌菜,每个店面基本都有自己的特色菜,我们将这种数据也保存出来,用另外的一张数据表储存。
最后一部分我们要抓取的数据是用户的评论,这部份数据对我们来说是太有价值的,将来我们可以通过对这部份数据的剖析,提取更多关于店家的信息。我们要抓取的这部份信息有:评论者爱称、星级、评论内容、评论时间,如果有图片,我们也要将图片的地址以列表的方式存出来。
2.2 创建数据表
我们储存数据使用的数据库是Mysql,Python有相关的ORM,项目中我们使用peewee。但是在构建数据表时建议采用原生的sql,这样我们能灵活的控制数组属性,设置引擎和字符编码格式等。使用Python的ORM也可以达到疗效,但是ORM是对数据库层的封装,像sqlite、sqlserver数据库和Mysql还是有些许差异的,使用ORM只能使用这种数据库共有的部份。下面是储存数据须要用到的数据表sql:
CREATE TABLE `merchant` ( #商家表
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL COMMENT '商家名称',
`address` varchar(255) NOT NULL COMMENT '地址',
`website_address` varchar(255) NOT NULL COMMENT '网址',
`website_address_hash` varchar(32) NOT NULL COMMENT '网址hash',
`mobile` varchar(32) NOT NULL COMMENT '电话',
`business_hours` varchar(255) NOT NULL COMMENT '营业时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4;
CREATE TABLE `recommended_dish` ( #推荐菜表
`id` int(11) NOT NULL AUTO_INCREMENT,
`merchant_id` int(11) NOT NULL COMMENT '商家id',
`name` varchar(255) NOT NULL COMMENT '推荐菜名称',
PRIMARY KEY (`id`),
KEY `recommended_dish_merchant_id` (`merchant_id`),
CONSTRAINT `recommended_dish_ibfk_1` FOREIGN KEY (`merchant_id`) REFERENCES `merchant` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=309 DEFAULT CHARSET=utf8mb4;
CREATE TABLE `evaluate` ( #评论表
`id` int(11) NOT NULL AUTO_INCREMENT,
`merchant_id` int(11) NOT NULL COMMENT '商家id',
`user_name` varchar(255) DEFAULT '' COMMENT '评论人昵称',
`evaluate_time` datetime NOT NULL COMMENT '评论时间',
`content` varchar(10000) DEFAULT '' COMMENT '评论内容',
`star` tinyint(4) DEFAULT '0' COMMENT '星级',
`image_list` varchar(1000) DEFAULT '' COMMENT '图片列表',
PRIMARY KEY (`id`),
KEY `evaluate_merchant_id` (`merchant_id`),
CONSTRAINT `evaluate_ibfk_1` FOREIGN KEY (`merchant_id`) REFERENCES `merchant` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8427 DEFAULT CHARSET=utf8mb4;
相应的我们也可以使用Python的ORM创建管理数据表,后边具体剖析到代码时会提到peewee对mysql数据库的一些常用操做,比如查询数据,插入数据库数据并返回id;批量插入数据库等,读者可采集相关资料系统学习。
meituan_spider/models.py代码:
from peewee import *
# 连接数据库
db = MySQLDatabase("meituan_spider", host="127.0.0.1", port=3306, user="root", password="root", charset="utf8")
class BaseModel(Model):
class Meta:
database = db
# 商家表,用来存放商家信息
class Merchant(BaseModel):
id = AutoField(primary_key=True, verbose_name="商家id")
name = CharField(max_length=255, verbose_name="商家名称")
address = CharField(max_length=255, verbose_name="商家地址")
website_address = CharField(max_length=255, verbose_name="网络地址")
website_address_hash = CharField(max_length=32, verbose_name="网络地址的md5值,为了快速索引")
mobile = CharField(max_length=32, verbose_name="商家电话")
business_hours = CharField(max_length=255, verbose_name="营业时间")
# 商家推荐菜表,存放菜品的推荐信息
class Recommended_dish(BaseModel):
merchant_id = ForeignKeyField(Merchant, verbose_name="商家外键")
name = CharField(max_length=255, verbose_name="推荐菜名称")
# 用户评价表,存放用户的评论信息
class Evaluate(BaseModel):
id = CharField(primary_key=True)
merchant_id = ForeignKeyField(Merchant, verbose_name="商家外键")
user_name = CharField(verbose_name="用户名")
evaluate_time = DateTimeField(verbose_name="评价时间")
content = TextField(default="", verbose_name="评论内容")
star = IntegerField(default=0, verbose_name="评分")
image_list = TextField(default="", verbose_name="图片")
if __name__ == "__main__":
db.create_tables([Merchant, Recommended_dish, Evaluate])
3.代码实现和解读
代码比较简单,但是使代码运行上去,需要安装前面提及的工具包:selenium、scrapy,另外使用peewee也须要安装,这些包都可以通过pip进行安装;另外selenium驱动浏览器还须要安装相应的driver,因为我本地使用的是chrome浏览器,所以我下载了相关版本的chromedriver,这个上面会使用到。请读者自行查阅python操作selenium须要做的打算工作,先自动搭建好相关环境。接下来详尽剖析代码;源代码如下:
<p>from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.common.exceptions import NoSuchElementException
from scrapy import Selector
from models import *
import hashlib
import os
import re
import time
import json
chrome_options = Options()
# 设置headless模式,这种方式下无启动界面,能够加速程序的运行
# chrome_options.add_argument("--headless")
# 禁用gpu防止渲染图片
chrome_options.add_argument('disable-gpu')
# 设置不加载图片
chrome_options.add_argument('blink-settings=imagesEnabled=false')
# 通过页面展示的像素数计算星级
def star_num(num):
numbers = {
"16.8": 1,
"33.6": 2,
"50.4": 3,
"67.2": 4,
"84": 5
}
return numbers.get(num, 0)
# 解析商家内容
def parse(merchant_id):
weblink = "https://www.meituan.com/meishi/{}/".format(merchant_id)
# 启动selenium
browser = webdriver.Chrome(executable_path="/Users/guozhaoran/python/tools/chromedriver", options=chrome_options)
browser.get(weblink)
# 不重复爬取数据
hash_weblink = hashlib.md5(weblink.encode(encoding='utf-8')).hexdigest()
existed = Merchant.select().where(Merchant.website_address_hash == hash_weblink)
if (existed):
print("数据已经爬取")
os._exit(0)
time.sleep(2)
# print(browser.page_source) #获取到网页渲染后的内容
sel = Selector(text=browser.page_source)
# 提取商家的基本信息
# 商家名称
name = "".join(sel.xpath("//div[@id='app']//div[@class='d-left']//div[@class='name']/text()").extract()).strip()
detail = sel.xpath("//div[@id='app']//div[@class='d-left']//div[@class='address']//p/text()").extract()
address = "".join(detail[1].strip())
mobile = "".join(detail[3].strip())
business_hours = "".join(detail[5].strip())
# 保存商家信息
merchant_id = Merchant.insert(name=name, address=address, website_address=weblink,
website_address_hash=hash_weblink, mobile=mobile, business_hours=business_hours
).execute()
# 获取推荐菜信息
recommended_dish_list = sel.xpath(
"//div[@id='app']//div[@class='recommend']//div[@class='list clear']//span/text()").extract()
# 遍历获取到的数据,批量插入数据库
dish_data = [{
'merchant_id': merchant_id,
'name': i
} for i in recommended_dish_list]
Recommended_dish.insert_many(dish_data).execute()
# 也可以遍历list,一条条插入数据库
# for dish in recommended_dish_list:
# Recommended_dish.create(merchant_id=merchant_id, name=dish)
# 查看链接一共有多少页的评论
page_num = 0
try:
page_num = sel.xpath(
"//div[@id='app']//div[@class='mt-pagination']//ul[@class='pagination clear']//li[last()-1]//span/text()").extract_first()
page_num = int("".join(page_num).strip())
# page_num = int(page_num)
except NoSuchElementException as e:
print("改商家没有用户评论信息")
os._exit(0)
# 当有用户评论数据,每页每页的读取用户数据
if (page_num):
i = 1
number_pattern = re.compile(r"\d+\.?\d*")
chinese_pattern = re.compile(u"[\u4e00-\u9fa5]+")
illegal_str = re.compile(u'[^0-9a-zA-Z\u4e00-\u9fa5.,,。?“”]+', re.UNICODE)
while (i