我用python爬取了敦煌网

2023-12-04 13:20
文章标签 python 我用 爬取 敦煌

本文主要是介绍我用python爬取了敦煌网,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

做跨境电商,产品的市场行情是非常关键的指标,无论是新品开发还是市场调研都有需求,那么今天我们就来做个敦煌网的产品价格与销量查询的工具。

一、基础版本

其实敦煌网是很客气的网站,基本上没有做针对的反爬措施,既然别人这么客气,我们也要懂得礼貌,做爬虫的同学都知道爬虫的基本礼仪。那就是该停就停,能在晚上没有太多人的时候运行就放在人少的时候,频率不要太高。不过还好我们的工具,一开始就打算按照关键词进行爬取,所以对网站的负担不会太重,可以放心的使用。

话不多说,先上代码

import requests
from bs4 import BeautifulSoup
import re
from urllib.parse import quote_plus
import 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:passfor 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写的,现在运行还是能够成功。这个版本,只获取的价格与销量。

二、第一次重构

其实很早就想重构一下,一直没有动力,大概是2017年3月的时候,有朋友问我对标题是怎么做的,终于找到理由重构一下了, 简单重构了下,新代码比较丑,数据储存还有bug, 当时太晚了,就没优化了,实现了获取产品标题,价格,起订量,销量,好评,店铺地址,卖家名,店铺好评率的获取。


import requests
from bs4 import BeautifulSoup
import re
from urllib.parse import quote_plus
import sys
from 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").textmin_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 0seller = 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 0data = {'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").textmin_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 0seller = 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

BeautifulSoupcss selector其实还是很好用的,当然,解析速度是相当来说慢了点,不过影响不是太大。后面我们会用lxmlxpath来重构,速度会好很多。对于新手,或者前端不是太理解的人来说,做爬虫还是比较坑的,我的经验来说,做爬虫最好还是要懂点前端, 当然懂得越多越好,爬虫与前端的反爬虫一直是这样相爱相杀,所以你越了解你的敌人,你就越得心就手。

三、用类的思想做个小框架

前面的内容基本已经能满足我们的需求了,但是类的思想可以让我们做到解耦,功能模块更清晰。
先上一个scrapy的框架图来镇楼:

主要内容有scrapy引擎,scheduler调度器,itempipline数据处理,downloader下载,spiders爬虫程序。
我们就模仿这个框架做一个简单的:

main.py # 主程序
url_manager.py # url管理器
html_downloader.py # 下载器 相当于scrapy的downloader
html_parser.py # 网页解析器 scrapy的解析直接就是在spiders里
html_outputer.py # 数据处理器 相当于scrapy的item pipeline

有了这几个类,我们已经可以完成一个简单的框架了。main.py 里主要是保证任务的进行。

import url_manager
import html_downloader
import html_outputer
import 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 = 1self.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 += 1except 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").textmin_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包里有相应的方法,可以直接使用。

就这样,整体项目被我们用小框架实现了。

后记

下一篇,我们用python自带的图形库tk来实现一个界面,方便普通用法使用。大家记得关注我公众号 Python与跨境电商 ,想要 源码可以在公众号 Python与跨境电商 留言输入 0020 获取。

这篇关于我用python爬取了敦煌网的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/453600

相关文章

Python 字符串占位

在Python中,可以使用字符串的格式化方法来实现字符串的占位。常见的方法有百分号操作符 % 以及 str.format() 方法 百分号操作符 % name = "张三"age = 20message = "我叫%s,今年%d岁。" % (name, age)print(message) # 我叫张三,今年20岁。 str.format() 方法 name = "张三"age

一道经典Python程序样例带你飞速掌握Python的字典和列表

Python中的列表(list)和字典(dict)是两种常用的数据结构,它们在数据组织和存储方面有很大的不同。 列表(List) 列表是Python中的一种有序集合,可以随时添加和删除其中的元素。列表中的元素可以是任何数据类型,包括数字、字符串、其他列表等。列表使用方括号[]表示,元素之间用逗号,分隔。 定义和使用 # 定义一个列表 fruits = ['apple', 'banana

Python应用开发——30天学习Streamlit Python包进行APP的构建(9)

st.area_chart 显示区域图。 这是围绕 st.altair_chart 的语法糖。主要区别在于该命令使用数据自身的列和指数来计算图表的 Altair 规格。因此,在许多 "只需绘制此图 "的情况下,该命令更易于使用,但可定制性较差。 如果 st.area_chart 无法正确猜测数据规格,请尝试使用 st.altair_chart 指定所需的图表。 Function signa

python实现最简单循环神经网络(RNNs)

Recurrent Neural Networks(RNNs) 的模型: 上图中红色部分是输入向量。文本、单词、数据都是输入,在网络里都以向量的形式进行表示。 绿色部分是隐藏向量。是加工处理过程。 蓝色部分是输出向量。 python代码表示如下: rnn = RNN()y = rnn.step(x) # x为输入向量,y为输出向量 RNNs神经网络由神经元组成, python

python 喷泉码

因为要完成毕业设计,毕业设计做的是数据分发与传输的东西。在网络中数据容易丢失,所以我用fountain code做所发送数据包的数据恢复。fountain code属于有限域编码的一部分,有很广泛的应用。 我们日常生活中使用的二维码,就用到foutain code做数据恢复。你遮住二维码的四分之一,用手机的相机也照样能识别。你遮住的四分之一就相当于丢失的数据包。 为了实现并理解foutain

python 点滴学

1 python 里面tuple是无法改变的 tuple = (1,),计算tuple里面只有一个元素,也要加上逗号 2  1 毕业论文改 2 leetcode第一题做出来

Python爬虫-贝壳新房

前言 本文是该专栏的第32篇,后面会持续分享python爬虫干货知识,记得关注。 本文以某房网为例,如下图所示,采集对应城市的新房房源数据。具体实现思路和详细逻辑,笔者将在正文结合完整代码进行详细介绍。接下来,跟着笔者直接往下看正文详细内容。(附带完整代码) 正文 地址:aHR0cHM6Ly93aC5mYW5nLmtlLmNvbS9sb3VwYW4v 目标:采集对应城市的

python 在pycharm下能导入外面的模块,到terminal下就不能导入

项目结构如下,在ic2ctw.py 中导入util,在pycharm下不报错,但是到terminal下运行报错  File "deal_data/ic2ctw.py", line 3, in <module>     import util 解决方案: 暂时方案:在终端下:export PYTHONPATH=/Users/fujingling/PycharmProjects/PSENe

将一维机械振动信号构造为训练集和测试集(Python)

从如下链接中下载轴承数据集。 https://www.sciencedirect.com/science/article/pii/S2352340918314124 import numpy as npimport scipy.io as sioimport matplotlib.pyplot as pltimport statistics as statsimport pandas

Python利用qq邮箱发送通知邮件(已封装成model)

因为经常喜欢写一些脚本、爬虫之类的东西,有需要通知的时候,总是苦于没有太好的通知方式,虽然邮件相对于微信、短信来说,接收性差了一些,但毕竟免费,而且支持html直接渲染,所以,折腾了一个可以直接使用的sendemail模块。这里主要应用的是QQ发邮件,微信关注QQ邮箱后,也可以实时的接收到消息,肾好! 好了,废话不多说,直接上代码。 # encoding: utf-8import lo