每当双十一、双十二,看着淘宝、天猫、京东网页上琳琅满目的商品,经常有人会因为选择困难症不知道该购买什么好。而且购物网站商品的排列顺序经常会受到人为控制。因此,一个具有通过输入关键字,筛选相关热门产品并按热门程度排序的程序是有存在意义的。 本程序以天猫为例,使用Python语言开发,利用Scrapy框架爬取网页信息,利用Tkinter框架构建程序GUI。源代码已上传至GitHub:https://github.com/HirojiSawatari/FindGoods 最终界面如下所示:
一、框架安装 Tkinter框架为内置模块,无需额外安装,而Scrapy框架安装方法见官方链接:https://doc.scrapy.org/en/latest/intro/install.html
二、Scrapy配置 构建项目后,由于天猫具有反爬取措施,首先需要在配置文件settings.py中输入以下代码:
1
2
3
4
5
6
7
8
ROBOTSTXT_OBEY = False
COOKIES_ENABLED = False
USER_AGENT = 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.89 Safari/537.36'
同时,为了输出爬取得到的信息,同样需要在setting.py中输入输出文件信息:
1
2
3
FEED_URI = u'goods.csv'
FEED_FORMAT = 'CSV'
配置文件配置完毕。
三、开始爬取 首先建立spider,爬取之前,设置name,爬取网站url等信息,并设置爬取时间间隔防止被ban。
1
2
3
4
5
6
name = "FindGoods"
download_delay = 4
allowed_domains = ["tmall.com" ]
start_urls = [
"https://www.tmall.com/"
]
确定我们需要爬取的商品相关信息。观察天猫搜素结果页,我们可以获取到的信息包括商品名称、店铺名称、价格、月成交量和评论数。为了实现之后点击item跳转购买界面的功能,我们还应当获取该商品购买界面的url信息。此外,我们还需预留分数字段以存储根据商品成交量和评论数计算得到的分数信息。确定爬取信息之后定义item.py如下:
1
2
3
4
5
6
7
name = Field()
shop = Field()
price = Field()
trading = Field()
review = Field()
url = Field()
score = Field()
继续观察天猫搜素结果页,我们可以通过审查页面要素得到我们需得到的商品信息在页面中的位置。Scrapy中页面位置信息通过xpath获取。由于天猫电器城情况特殊,我们还需要单独对电子产品页面进行单独分析。最终分析得到的要素爬取xpath信息如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
gifts = sel.xpath('//*[@id="J_ItemList"]/div[@class="product "]' )
for gift in gifts:
name = gift.xpath('div/p[@class="productTitle"]/a/@title' ).extract()
if not name:
name = gift.xpath('div/div[@class="productTitle productTitle-spu"]/a[1]/text()' ).extract()
shop = gift.xpath('div/div[@class="productShop"]/a[@class="productShop-name"]/text()' ).extract()
price = gift.xpath('div/p[@class="productPrice"]/em/@title' ).extract()
trading = gift.xpath('div/p[@class="productStatus"]/span[1]/em/text()' ).extract()
review = gift.xpath('div/p[@class="productStatus"]/span[2]/a/text()' ).extract()
url = gift.xpath('div/p[@class="productTitle"]/a/@href' ).extract()
if not url:
url = gift.xpath('div/div[@class="productTitle productTitle-spu"]/a[1]/@href' ).extract()
获取信息后需要存储,同时计算商品评分。我们此处评分标准采用(月交易量×2+评论数)的方法计算。因为在html中获取的月交易量信息和评论数信息可能带有中文字符,例如“万”、“笔”等,我们还需剔除这些无关信息后转换为float数据类型进行计算。具体代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
item['name' ] = [na.encode(sys.getfilesystemencoding()) for na in name]
tempshop = str(shop[0 ].encode(sys.getfilesystemencoding()))
item['shop' ] = tempshop.strip('\n' )
item['price' ] = price
item['url' ] = 'https:' + url[0 ]
tradnum = 0
if trading:
tradstr = str(trading[0 ].encode(sys.getfilesystemencoding()))
item['trading' ] = tradstr
biindex = tradstr.index('\xb1\xca' )
tradstr = tradstr[:biindex]
if '\xcd\xf2' in tradstr:
wanindex = tradstr.index('\xcd\xf2' )
tradstr = tradstr[:wanindex]
tradnum = tradnum + string.atof(tradstr) * 10000
else :
tradnum = tradnum + string.atof(tradstr)
revinum = 0
if review:
revistr = str(review[0 ].encode(sys.getfilesystemencoding()))
item['review' ] = revistr
if '\xcd\xf2' in revistr:
wanindex2 = revistr.index('\xcd\xf2' )
revistr = revistr[:wanindex2]
revinum = revinum + string.atof(revistr) * 10000
else :
revinum = revinum + string.atof(revistr)
score = revinum + (tradnum * 2 )
item['score' ] = round(score)
yield (item)
一页商品信息是不够的,因此我们观察后两页url可以发现,“s=60”表示第二页,“s=120”表示第三页,且“q=”后面的信息为商品关键字信息。因此我们可以依此设置后两页url并进行递归实现对后两页商品信息的自动爬取。
1
2
3
4
5
6
7
8
9
good = response.url[(response.url.index("q=" ) + 2 ):response.url.index("&type=p&v" )]
next_page_urls = [
"https://list.tmall.com/search_product.htm?spm=a220m.1000858.0.0.0HVJLN&s=60&q=" + good + "&sort=s&style=g&from=mallfp..pc_1_searchbutton&type=pc#J_Filter" ,
"https://list.tmall.com/search_product.htm?spm=a220m.1000858.0.0.Zt2HlG&s=120&q=" + good + "&sort=s&style=g&from=mallfp..pc_1_searchbutton&type=pc#J_Filter"
]
for next_page_url in next_page_urls:
yield Request(next_page_url, callback=self.parse)
爬虫构建完毕,在根目录建立runscrapy.py并输入以下代码后,执行该文件即可实现爬取相关信息并存储信息至根目录goods.csv的功能。
1
2
3
from scrapy import cmdline
cmdline.execute("scrapy crawl FindGoods" .split())
爬虫功能构建完毕。
四、GUI构建 为了界面美观,我们使用Tkinter为该爬虫程序构建了一个GUI,即main.py。需包括以下功能:
1、关键词输入与执行爬虫 关键词输入框构建代码如下:
1
2
3
var = StringVar()
e = Entry(root, textvariable=var).grid(row=2 )
确认按钮构建代码如下:
1
2
Button(root, text="开始查询" , command=startSpider).grid(row=3 )
爬虫执行函数如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def startSpider () :
good = var.get()
temp = open('tempgoods.temp' , 'w' )
temp.write(good.encode(sys.getfilesystemencoding()))
temp.close()
if os.path.exists('goods.csv' ):
csvfile = open('goods.csv' , 'w' )
csvfile.truncate()
os.system("runscrapy.py" )
同时,为了让爬虫按照给定的关键字进行爬取,需要让爬虫读取通过该临时文件传递过来的关键字信息。因此对goodspider.py进行以下修改:
1
2
3
4
5
temp = open('tempgoods.temp' , 'r' )
good = temp.read()
temp.close()
url = "https://list.tmall.com/search_product.htm?q=" + good + "&type=p&vmarket=&spm=875.7931836%2FA.a2227oh.d100&from=mallfp..pc_1_searchbutton"
2、爬取结果显示 爬取结果通过TreeView控件显示。控件实现代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
tree = ttk.Treeview(root, show="headings" , columns=('店名' , '商品名' , '购买链接' , '价格' , '评分' , '月交易量' , '评论数' ))
ysb = ttk.Scrollbar(root, orient='vertical' , command=tree.yview)
xsb = ttk.Scrollbar(root, orient='horizontal' , command=tree.xview)
tree.configure(yscroll=ysb.set, xscroll=xsb.set)
tree.column('店名' , width=100 , anchor='center' )
tree.column('商品名' , width=250 , anchor='center' )
tree.column('购买链接' , width=125 , anchor='center' )
tree.column('价格' , width=50 , anchor='center' )
tree.column('评分' , width=65 , anchor='center' )
tree.column('月交易量' , width=55 , anchor='center' )
tree.column('评论数' , width=55 , anchor='center' )
tree.heading('店名' , text='店名' )
tree.heading('商品名' , text='商品名' )
tree.heading('购买链接' , text='购买链接' )
tree.heading('价格' , text='价格' )
tree.heading('评分' , text='评分' )
tree.heading('月交易量' , text='月交易量' )
tree.heading('评论数' , text='评论数' )
vbar = ttk.Scrollbar(root, orient=VERTICAL, command=tree.yview)
tree.configure(yscrollcommand=vbar.set)
refreshTree()
3、结果表格的刷新 每次爬取结束获取新的信息后需要对结果表格内容进行更新。而且得到的商品信息需要根据各商品的得分进行从大到小的排序。更新函数如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def refreshTree () :
items = tree.get_children()
for item in items:
tree.delete(item)
if os.path.exists('goods.csv' ):
csvfile = file('goods.csv' , 'rb' )
if csvfile:
lines = []
reader = csv.reader(csvfile)
i = 0
for line in reader:
if i > 0 :
lines.append(line)
i = i + 1
for j in range(len(lines) - 1 , 0 , -1 ):
for k in range(j):
if string.atof(lines[k][4 ]) < string.atof(lines[k + 1 ][4 ]):
lines[k], lines[k + 1 ] = lines[k + 1 ], lines[k]
i = 0
for line in lines:
tree.insert('' , i, values=(
line[0 ].decode(sys.getfilesystemencoding()), line[1 ].decode(sys.getfilesystemencoding()),
line[2 ].decode(sys.getfilesystemencoding()), line[3 ].decode(sys.getfilesystemencoding()),
line[4 ].decode(sys.getfilesystemencoding()), line[5 ].decode(sys.getfilesystemencoding()),
line[6 ].decode(sys.getfilesystemencoding())))
i = i + 1
4、点击表格内商品直接跳转至购买界面 TreeView点击监听代码如下:
1
2
3
4
5
6
7
8
9
tree.bind("<Double-1>" , onDBClick)
跳转函数代码如下:
def onDBClick (event) :
item = tree.selection()[0 ]
info = tree.item(item, "values" )
url = info[2 ]
webbrowser.open_new(url)
以上就是构建该项目的全部步骤。执行main.py即可实现项目的启动。