實戰(zhàn)第一篇笋轨,以下廚房網(wǎng)頁端為例,任務(wù)目標(biāo):
- 爬取下廚房網(wǎng)頁端所有的菜品
- 創(chuàng)建基本的工具類走孽,數(shù)據(jù)管理工具
- 將爬取的數(shù)據(jù)結(jié)構(gòu)化保存到數(shù)據(jù)庫中
以下是下廚房的首頁:
從網(wǎng)頁結(jié)構(gòu)上分析,分類是個很好的爬取所有菜品的入口,點開菜譜分類:
點擊其中一個分類:
到此术陶,基本思路已經(jīng)很清晰:
- 爬取所有的分類
- 通過分類進(jìn)入菜品列表,爬取該分類下所有菜品
難點有兩個:
- 分類頁有個【展開全部】的action煤痕,如何得到一個大分類下的所有二級分類梧宫?
- 如何爬取一個二級分類下的所有頁數(shù)據(jù)?
問題1
打開瀏覽器摆碉,查看分類頁面的源碼:
不難發(fā)現(xiàn)祟敛,點擊『展開全部』后隱藏的數(shù)據(jù)都是存放在
<div class='cates-list-all clearfix hidden'>
下的,只要取該div
下的數(shù)據(jù)兆解,變可以得到全量的分類馆铁,其結(jié)構(gòu)如下圖:
到此問題1已解決
問題2
首先查看分頁部分的源代碼:
只要從每頁中取出下一頁的鏈接,依次爬取每一頁的內(nèi)容即可锅睛,知道下一頁的鏈接為空時埠巨,及表示已是最后一頁,該二級分類下的所有菜品都爬完了现拒。
開始寫代碼
1. 創(chuàng)建project
scrapy startproject cook
文件結(jié)構(gòu)
(env) ? cook tree
.
├── scrapy.cfg
└── cook
├── __init__.py
├── items.py
├── middlewares.py
├── pipelines.py
├── settings.py
└── spiders
└── __init__.py
2 directories, 7 files
關(guān)于scrapy的基本內(nèi)容這里不多做介紹辣垒,不熟悉的同學(xué)可以查閱本系列第二篇文章:Python爬蟲系列(三):python scrapy介紹和使用
2. 創(chuàng)建分類表結(jié)構(gòu)
command = (
"CREATE TABLE IF NOT EXISTS {} ("
"`id` INT(8) NOT NULL AUTO_INCREMENT,"
"`name` CHAR(20) NOT NULL COMMENT '分類名稱',"
"`url` TEXT NOT NULL COMMENT '分類url',"
"`category` CHAR(20) NOT NULL COMMENT '父級分類',"
"`category_id` INT(8) NOT NULL COMMENT '分類id',"
"`create_time` DATETIME NOT NULL,"
"PRIMARY KEY(id),"
"UNIQUE KEY `category_id` (`category_id`)"
") ENGINE=InnoDB".format(config.category_urls_table)
)
self.sql.create_table(command)
3. 解析部分
def parse_all(self, response):
if response.status == 200:
file_name = '%s/category.html' % (self.dir_name)
self.save_page(file_name, response.body)
categorys = response.xpath("http://div[@class='cates-list-all clearfix hidden']").extract()
for category in categorys:
sel_category = Selector(text = category)
category_father = sel_category.xpath("http://h4/text()").extract_first().strip()
items = sel_category.xpath("http://ul/li/a").extract()
for item in items:
sel = Selector(text = item)
url = sel.xpath("http://@href").extract_first()
name = sel.xpath("http://text()").extract_first()
_id = re.compile('/category/(.*?)/').findall(url)[0]
dt = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
msg = (None , name, url, category_father, _id, dt)
command = ("INSERT IGNORE INTO {} "
"(id, name, url, category, category_id, create_time)"
"VALUES(%s,%s,%s,%s,%s,%s)".format(config.category_urls_table)
)
self.sql.insert_data(command, msg)
根據(jù)問題1里面的描述,我們要從http://www.xiachufang.com/category/
中找出所有的二級分類以及其連接印蔬,每個一級分類對應(yīng)的數(shù)據(jù)都在<div class='cates-list-all clearfix hidden'>
下勋桶,參考代碼:
categorys = response.xpath("http://div[@class='cates-list-all clearfix hidden']").extract()
接著我們將所有二級分類的名稱,對應(yīng)url取出存入數(shù)據(jù)中。
4.爬取菜品
先取出之前存儲到數(shù)據(jù)控的所有分類:
command = "SELECT * from {}".format(config.category_urls_table)
data = self.sql.query(command)
解析部分:
def parse_all(self, response):
utils.log(response.url)
if response.status == 200:
file_name = '%s/category.html' % (self.dir_name)
self.save_page(file_name, response.body)
recipes = response.xpath("http://div[@class='normal-recipe-list']/ul/li").extract()
self.parse_recipes(recipes)
nextPage = response.xpath("http://div[@class='pager']/a[@class='next']/@href").extract_first()
if nextPage:
yield Request(
url = self.base_url + nextPage,
headers = self.header,
callback = self.parse_all,
errback = self.error_parse,
)
def parse_recipes(self, recipes):
for recipe in recipes:
sel = Selector(text = recipe)
name = sel.xpath("http://p[@class='name']/text()").extract_first().strip()
url = sel.xpath("http://a[1]/@href").extract_first()
img = sel.xpath("http://div[@class='cover pure-u']/img/@data-src").extract_first()
item_id = re.compile("/recipe/(.*?)/").findall(url)[0]
source = sel.xpath("http://p[@class='ing ellipsis']/text()").extract_first().strip()
score = sel.xpath("http://p[@class='stats']/span/text()").extract_first().strip()
dt = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
msg = (None, name, url, img, item_id, source, score, dt)
command = ("INSERT IGNORE INTO {} "
"(id, name, url, img, item_id, source, score, create_time)"
"VALUES(%s,%s,%s,%s,%s,%s,%s,%s)".format(config.item_list_table)
)
self.sql.insert_data(command, msg)
通過分析源代碼例驹,先取出每一頁的所有菜品信息部分:
recipes = response.xpath("http://div[@class='normal-recipe-list']/ul/li").extract()
通過方法parse_recipes
解析出該頁所有的菜品信息捐韩,并存儲。
同時取出『下一頁』對應(yīng)的url(nextPage),如果nextPage不為空(還有下一頁)鹃锈,則接著爬取下一頁內(nèi)容荤胁。
5.執(zhí)行爬蟲程序
依次執(zhí)行以上的兩個spider程序,下廚房所有的菜品就到手了屎债,有興趣的同學(xué)也可以接著爬取菜品的詳情頁內(nèi)容仅政。
源碼地址:https://github.com/sam408130/xcf_crawler
交流學(xué)習(xí)qq:197329984
再次聲明,本篇文章僅用于交流學(xué)習(xí)盆驹,請勿用于任何商業(yè)用途
接下來文章內(nèi)容預(yù)告:
- 如果爬取京東app(https通信)數(shù)據(jù)
- 如何添加代理
- scrapy的管理圆丹,部署