您的位置:首页 > 编程语言 > Python开发

使用Python将淘宝网“拍卖商品”按拍卖“剩余时间”排序

2011-09-11 23:11 323 查看
问题描述:

在淘宝网(www.taobao.com)中搜索商品时,比如搜索“诺基亚5230”在搜索结果的“交易”中可以选择“拍卖”。



之后得到的结果列出来的是一个商品列表,它可以按以下项目升序或降序排列:



点击列表中某个项目后,打开的页面一个商品的详细信息,由于当前交易为“拍卖”,所以得到的信息中会有“剩余时间”:



注意剩余时间不是在搜索结果同一个页面中显示的,而是在点开具体商品的链接之后才能获取到的。现在有一些用户有比较特殊的需求,他们希望搜索到的商品能按照拍卖剩余时间排序,而淘宝网并没有提供这样的功能。而用户只需要得到一张按照剩余时间排序的列表,上面有对应商品项目的地址,以便能及时知道拍卖剩余时间最短的商品项目。

解决方案:

由于要求只是一个商品项目列表,尽量用简单办法处理,所以选择用python脚本去解决这个问题。
由于淘宝数据库并非公开,所以只能通过下载页面去获取相关的信息。从商品项目搜索结果的URL(由用户输入)载入搜索结果的页面,然后由那个页面获取对应商品项目的页面列表,然后从那些页面列表中读取出剩余时间的数据。最后用剩余时间作比较,对商品项目进行排序,排序后的页面列表。

中间遇到的问题:

获取商品项目的URL列表部分很顺利地完成,使用的是正则表达式提取我们需要的URL,然而按获取的URL列表各个地址载入商品详细信息后,却发现该页面显示商品拍卖“剩余时间”的部分并不是由html直接显示的,它使用了Javascript动态显示该页面。而python的urllib包没有提供解析Javascript的功能。这对信息读取造成了阻碍。

如何解决新的问题:

显然,如果又大费周折去编写模块解析Javascript肯定不合算。而且由于剩余时间是动态的信息,随时间而变,Javascript在页面中运行时肯定需要读取载入的html中的变量值,Javascript内容可能存放在另外一个URL中,但是变量值应该在html中也有。要寻找变量值的位置。但是这个变量又放在html页面的哪个部分呢?
经过尝试,我决定两次下载同一个商品的html页面。由于下载时间是不同的,同一个页面的内容也是不同的(里面Javascript的变量值不同)。我只需要找出两个页面的不同之处就能找到表示剩余时间的变量了。
使用fc.exe,下载两次的页面分别为1.htm、2.htm,使用fc 1.htm 2.htm后得到了以下结果:(在此仅展示找到时间部分)

***** 1.htm

"apiItemCollects": "http://count.taobao.com/counter2?keys=ICCP_1_13202536163",

"valTimeLeft": "546816",

"apiItemDesc":"http://dsc.taobaocdn.com/i1/130/020/13202536163/T1.ZinXopZXXXXXXXX.desc%7Cvar%5Edesc%3Bsign%5Ed58a63bb0a20e89994

***** 2.htm

"apiItemCollects": "http://count.taobao.com/counter2?keys=ICCP_1_13202536163",

"valTimeLeft": "546794",

"apiItemDesc":"http://dsc.taobaocdn.com/i1/130/020/13202536163/T1.ZinXopZXXXXXXXX.desc%7Cvar%5Edesc%3Bsign%5Ed58a63bb0a20e89994

*****

经过检验,html中的valTimeLeft这个变量即为拍卖剩余时间的秒数,html中并没有计算并显示“剩余 ? 天 ? 小时”的Javascript,它应该是被放在了不同的URL中,不过现在有了valTimeLeft已经足够了,直接通过正则表达式提取读出它后面那个数的数值就行了。最后对其进行排序。

程序的优化:

由于单线程下载处理速度过慢,多个商品项目将花费很长时间才能完成排序,因为访问的页面量非常大。故使用多线程同时下载多个页面提取信息,当然当中的同步又花费了一翻功夫,不过以前做过,还算轻车熟路。

代码实现:

GetUrlList.py --> 读入 UrlIn.txt中用户保存的搜索结果所在的URL(若有多个页面,则一行一个URL)

# -*- coding: cp936 -*-
import urllib
import re
import os

# 获取有效页面URL的正则表达式
strRe = r'<a stat=".*" +href ?= ?"(http://.+)" target="_blank" class="EventCanSelect" title="'

# 入口Url列表文件
fin = file('UrlIn.txt')
# 输出Url列表文件
fout = file('UrlOut.txt', 'w')

def getUrlList(strPage):
return re.findall(strRe, strPage)

def getPage(strUrl):
try:
f = urllib.urlopen(strUrl)
s = f.read()
f.close()
return s
except:
print 'Failed Searching Page: ' + strUrl
return ''

if __name__ == '__main__':
for i in fin.xreadlines():
url = i[:-1]    # 去掉换行
print 'Searching Page: ' + url
strPage = getPage(url)
for item in getUrlList(strPage):
fout.write(item + '\n')
print 'Task Finished!'
fout.close()
os.system('pause')


GetUrlList.py获取了所有搜索结果中商品的页面地址,并以一行一个URL的格式写入到了UrlOut.txt中。

getExpireTime.py从UrlOut.txt中每个URL页面获取对应商品的“剩余时间”,并将排序后的结果输出到result.htm中,同时把控制台输出保存一份到log.txt中。
# -*- coding: cp936 -*-
import urllib
import re
import os
import threading
import time

# 获取剩余时间的正则表达式
strReTime = r'"valTimeLeft": "([0-9]+)"'
# 从该文件中读入URL列表
fin = file('UrlOut.txt')
# 最大线程数
nMaxThread = 5

def getExpireTime(strPage):
try:
return int(re.search(strReTime, strPage).group(1))
except:
return -1

def getPage(strUrl):
try:
f = urllib.urlopen(strUrl)
s = f.read()
f.close()
return s
except:
print 'Failed Getting Page: ' + strUrl
return ''

# 限制线程数量的信号量
semaphore = threading.Semaphore(nMaxThread)
# 同步输出的锁
lockPrint = threading.Lock()
# 同步提交到链表的锁
lockCommit = threading.Lock()
# 用于排序并输出的链表
listItem = []
logOutPut = file('log.txt', 'w')
resOutPut = file('result.htm', 'w')

def safe_Print(strContent):
"""同步打印"""
lockPrint.acquire()
print strContent
logOutPut.write(strContent + '\n')
lockPrint.release()

def safe_Commit(tp):
"""同步提交到链表"""
lockCommit.acquire()
listItem.append(tp)
lockCommit.release()

class dlThread(threading.Thread):
def __init__(self, url):
threading.Thread.__init__(self)
self.url = url
def run(self):
strPage = getPage(self.url)
expireTime = getExpireTime(strPage)
safe_Print(str(expireTime) + '\t' + self.url)
safe_Commit((expireTime, self.url))
semaphore.release()

if __name__ == '__main__':
try:
nInitActiveCount = threading.active_count()
# print nInitActiveCount   # >>>1
for i in fin.xreadlines():
url = i[:-1]
th = dlThread(url)
semaphore.acquire()
th.start()
print '正在获取' + url

while threading.active_count() > nInitActiveCount:
time.sleep(1)    # 等待所有子线程退出

listItem.sort()
for item in listItem:
print str(item[0]) + ' ' + item[1]
resOutPut.write(str(item[0]) + ' ' + item[1] + '<br/>')
except:
print '运行过程发生异常!提前终止'
finally:
logOutPut.close()
resOutPut.close()
os.system('pause')


使用程序时,用户将搜索结果的URL按一行一个的方式复制到UrlIn.txt中并保存,然后先后运行GetUrlList.py和getExpireTime.py,输出的result.htm则是按拍卖剩余时间排序结果。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: