做跨境电商,产品的市场行情是非常关键的指标,无论是新品开发还是市场调研都有需求,那么今天我们就来做个敦煌网的产品价格与销量查询的工具。
import requestsfrom bs4 import BeautifulSoupimport refrom urllib.parse import quote_plusimport sysdef save_data(url,path='dhgate.csv',data=None): web_data = requests.get(url) soup = BeautifulSoup(web_data.text,'lxml') prices = [] orders = [] for item in soup.select('#proList .price'): m = re.search(r'(/d*./d*) - (/d*./d*)',item.text) if m: price = float(m.group(1))+float(m.group(2)) prices.append(round(price/2,2)) else: pass for item in soup.select('#proList .attribute'): m = re.search(r'Sold: (/d+)',item.text) if m: orders.append(m.group(1)) else: orders.append(None) for price, order in zip(prices,orders): data = { 'price': price, 'order': order } print(data) with open(path,'a') as f: f.write('{},{}/n'.format(data['price'],data['order']))def get_data(key_word,page_num): key_word = quote_plus(key_word) urls = ['http://www.dhgate.com/w/{}/{}.html'.format(key_word,str(i)) for i in range(page_num)] for url in urls: save_data(url,key_word+'.csv')if __name__ == '__main__': key_word,page_num = sys.argv[1:3] get_data(key_word,int(page_num))
内容比较简单,为了让大家不至于看的太累,注释什么的大多被我删除了。 下面我们来简单的讲解下这段代码。 首先,我们导入要用的包:import requests # requests包主要用来获取网页内容from bs4 import BeautifulSoup # BeautifulSoup用来解释网页内容import re # re包是用正则来辅助解析用from urllib.parse import quote_plus # quote_plus用来处理关键词import sys # sys用来获取命令行的参数
主要流程都在__main__
里面,我们通过sys
获取的关键词和页数,这里没有异常处理,其实应该对传入的参数进行异常处理下的。然后直接运行get_data
函数获取我们所需的数据。我们直接在get_data
函数里调用的save_data
把数据存储到csv
文件中。 这个是最早的版本,大概是在2016写的,现在运行还是能够成功。这个版本,只获取的价格与销量。import requestsfrom bs4 import BeautifulSoupimport refrom urllib.parse import quote_plusimport sysfrom numpy import meandef save_data(url,path='dhgate.csv',data=None): web_data = requests.get(url) soup = BeautifulSoup(web_data.text,'lxml') info = [] items = soup.find_all("div", "listitem") for item in items: title = item.find("h3").find("a").text # 标题 price = item.find("li","price").text # 价格 m = re.findall(r'(/d+/.*/d+)', price) price = mean(list(map(float, m))) # 计算均价 attribute = item.find("ul", "attribute").text min_order = re.findall(r'Min. Order: (/d+)', attribute)[0] # 起订量 order = re.findall(r'Sold: (/d+)', attribute) order = order[0] if len(order) > 0 else 0 # 订单量 feedback = item.find("span","reviewnum") feedback = re.findall(r"/d+", feedback.text)[0] if feedback else 0 seller = list(item.find("span","seller").stripped_strings)[-1] store_url = item.find("span","seller").find("a")['href'] store_feedback = item.find("li","feedback") store_feedback = re.findall(r"/d+/.*/d+", store_feedback.text)[0] if store_feedback else 0 data = { 'title': title, 'price': price, 'min_order': min_order, 'order': order, 'feedback': feedback, 'seller': seller, 'store_url': store_url, 'store_feedback': store_feedback } print(data) with open(path,'a') as f: f.write('{}/t{}/t{}/t{}/t{}/t{}/t{}/t{}/n'.format( data['title'], data['price'], data['min_order'], data['order'], data['feedback'], data['seller'], data['store_url'], data['store_feedback'] ))def get_data(key_word,page_num): key_word = quote_plus(key_word) urls = ['http://www.dhgate.com/w/{}/{}.html'.format(key_word,str(i)) for i in range(page_num)] for url in urls: save_data(url,key_word+'.csv')if __name__ == '__main__': key_word,page_num = sys.argv[1:3] get_data(key_word,int(page_num))
老样子,简单讲解下,其实主体和第一次写的没有太大差别,主要是字段解析这里,多添加了一些内容:for item in items: title = item.find("h3").find("a").text # 标题 price = item.find("li","price").text # 价格 m = re.findall(r'(/d+/.*/d+)', price) price = mean(list(map(float, m))) # 计算均价 attribute = item.find("ul", "attribute").text min_order = re.findall(r'Min. Order: (/d+)', attribute)[0] # 起订量 order = re.findall(r'Sold: (/d+)', attribute) order = order[0] if len(order) > 0 else 0 # 订单量 feedback = item.find("span","reviewnum") feedback = re.findall(r"/d+", feedback.text)[0] if feedback else 0 seller = list(item.find("span","seller").stripped_strings)[-1] store_url = item.find("span","seller").find("a")['href'] store_feedback = item.find("li","feedback") store_feedback = re.findall(r"/d+/.*/d+", store_feedback.text)[0] if store_feedback else 0
BeautifulSoup
的css selector
其实还是很好用的,当然,解析速度是相当来说慢了点,不过影响不是太大。后面我们会用lxml
的xpath
来重构,速度会好很多。对于新手,或者前端不是太理解的人来说,做爬虫还是比较坑的,我的经验来说,做爬虫最好还是要懂点前端, 当然懂得越多越好,爬虫与前端的反爬虫一直是这样相爱相杀,所以你越了解你的敌人,你就越得心就手。main.py # 主程序url_manager.py # url管理器html_downloader.py # 下载器 相当于scrapy的downloaderhtml_parser.py # 网页解析器 scrapy的解析直接就是在spiders里html_outputer.py # 数据处理器 相当于scrapy的item pipeline
有了这几个类,我们已经可以完成一个简单的框架了。main.py
里主要是保证任务的进行。import url_managerimport html_downloaderimport html_outputerimport html_parserclass SpiderMain(object): def __init__(self): self.urls = url_manager.UrlManager() self.downloader = html_downloader.HtmlDownloader() self.parser = html_parser.HtmlParser() self.outputer = html_outputer.HtmlOutputer() def craw(self, key_word, page_num): count = 1 self.urls.build_url(key_word, int(page_num)) while self.urls.has_new_url(): try: new_url = self.urls.get_new_url() print(f"craw {count} : {new_url}") html_cont = self.downloader.download(new_url) new_data = self.parser.parse(new_url, html_cont) self.outputer.collect_data(new_data) count += 1 except Exception as e: print("craw failed", e) self.outputer.to_csv() # return self.outputer.datasif __name__ == "__main__": spider = SpiderMain() print(spider.craw("women dress", "2"))
主程序内容比较简单,导入相应的类,构建了一个爬虫主类,传入关键词和页数,爬虫就愉快的开始爬网了。 核心就在这个craw
函数。 1. 首先url管理器构建一个初始url,告诉爬虫从哪儿开始爬取。 2. 然后爬虫开始看url管理器里有没有新的url,有就获取新的url,把新的url传入下载器进行下载。 3. 然后下载器把下载的数据传入解析器进行解析。 4. 数据处理器收集解析器解析出来的新数据。 5. 数据处理器保存数据到本地。url_manage.py
url管理器build_url
构建初始网址 add_new_url
添加新的url到管理器 has_new_url
检查管理器里有没有新的url get_new_url
从管理器里获取新的url show_urls
遍历管理器里的url 这里的功能实际上都是针对的__init__
里面设置的两个set
,这里没有使用数据库,使用数据库也是一样的效果。def __init__(self): self.new_urls = set() self.old_urls = set() self.site = 'http://www.dhgate.com/w/{0}/{1}.html'
html_downloader.py
下载器requests
包,使用其相关方法就完成了download
方法。html_parser.py
网页解析器def _get_new_data(self, page_url, soup): items = soup.find_all("div", "listitem") datas = [] for item in items: title = item.find("h3").find("a").text # 标题 product_url = HtmlParser.format_str( item.select("h3 > a.subject")[0].get("href")) price = item.find("li", "price").text # 价格 min_price, max_price = re.findall(r'(/d+/.*/d+)', price) # 最低价,最高价 attribute = item.find("ul", "attribute").text min_order = re.findall(r'Min. Order: (/d+)', attribute)[0] # 起订量 order = re.findall(r'Sold: (/d+)', attribute) order = order[0] if len(order) > 0 else 0 # 订单量 feedback = item.find("span", "reviewnum") feedback = re.findall(r"/d+", feedback.text)[0] if feedback else 0 # 产品好评 seller = list(item.find("span", "seller").stripped_strings)[-1] # 卖家 store_url = item.find("span", "seller").find("a")['href'] # 店铺链接 store_feedback = item.find("li", "feedback") store_feedback = re.findall( r"/d+/.*/d+", store_feedback.text)[0] if store_feedback else 0 # 店铺评价 data = { 'page_url': page_url, 'title': title, 'product_url': 'http:' + product_url, 'min_price': min_price, 'max_price': max_price, 'min_order': min_order, 'order': order, 'feedback': feedback, 'seller': seller, 'store_url': store_url, 'store_feedback': store_feedback } datas.append(data) return datas
就不多说了,parse
方法里引用_get_new_data
解析完成返回数据。html_outputer.py
数据处理器collect_data
收集前面处理好的数据 to_html
把数据输出成html格式 to_csv
把数据输出成csv格式,这个就和我们之前做的一样,不过这里使用了csv
包,效率更高def to_html(self): with open('output.html', 'w') as f: f.write("<html>") f.write("<body>") f.write("<table>") f.write("<tr>") for key in self.datas[0].keys(): f.write(f"<td>{key}</td>") f.write("</tr>") for data in self.datas: f.write("<tr>") for key, value in data.items(): f.write(f"<td>{value}</td>") f.write("</tr>") f.write("</table>") f.write("</body>") f.write("</html>") def to_csv(self, path="output.csv"): with open(path, 'w', newline="") as f: try: writer = csv.DictWriter(f, self.datas[0].keys()) except IndexError: print(self.datas[0].keys()) writer.writeheader() for data in self.datas: writer.writerow(data)
可以看到输出到html稍微麻烦点,主要是要写html
特有的标签,而csv就相当简单了,csv
包里有相应的方法,可以直接使用。tk
来实现一个界面,方便普通用法使用。大家记得关注我公众号,想要 源码可以在公众号后台输入 0020 获取。杭州市滨江区伟业路3号
业务热线(微信同号):18143453325
业务邮箱:2848414880@qq.com
业务QQ:2848414880
顶部
合作
微信
公众号