python-网页抓取

这两天去朋友家玩,他说到了最近接了个活儿,需要帮人家整理网上书单,而且好像有点麻烦的样子。我一看,嘛这个需求,我用脚本写一个给你啦

用 python 抓取网页书单

1、分析需求

需要将网页里面的特定的内容抓取,具体来说就是

限定平装书,需要书名 作者名 出版时间 售价 出版社 从书名(如果有的话)内容简介 ISBN码
这些内容在网页里面肯定是有的,所以我们只需要使用【正则表达式】将需要的内容提取出来即可。
然后他指定的内容范围为
关键字为音乐鉴赏,美术鉴赏,艺术创作。
这个。。amazon的参数好像蛮多。。。那我不如这里先偷个懒(那句话怎么说,KISS原则嘛),这里先将他的要求的网址记录下来
音乐鉴赏
美术鉴赏
艺术创作
好吧长得都好像啊。。。

2、设计功能

朋友要求是将上述的数据整理至一个excel,这种功能,上网找了一个python的封装的库应该就没有问题了。那么为了让其使用方便一点,我们这里最好支持一下交互界面什么的,依然打算拿pyqt撸一个出来,方便使用。
那么大概功能就是:

  • 将amazon中的网页爬下来
  • 将爬下来的数据进行处理,取出我们自己需要的部分。
  • 将数据整合到一个excel里
  • 友好的用户交互界面

3、具体实现

简单的脚本,就拿python来做吧,这里使用requests来将数据取下来,但是注意一点,amazon其实是有防护机制的,如果你贸然的使用get方法访问的话,amazon会返回一个这样的东西给你:

1
2
3
4
<!--
To discuss automated access to Amazon data please contact api-services-support@amazon.com.
For information about migrating to our APIs refer to our Marketplace APIs at https://developer.amazonservices.com.cn/index.html/ref=rm_5_sv, or our Product Advertising API at https://associates.amazon.cn/gp/advertising/api/detail/main.html/ref=rm_5_ac for advertising use cases.
-->

然后我尝试着伪造了一下头部,就绕过去了。。。。
然后就是提取这个页面里面的每个书对应的url,观察当前的页面,里面的url有一个特征词汇"a-link-normal s-access-detail-page a-text-normal",那么可以利用这个关键字,使用正则表达式将当前的数据提取出来。
由于我们要提取的是href中间的内容,我们首先要指定是**<\a>**中的内容,然后利用()将我们需要的内容返回

1
pageDetail = r'<a class="a-link-normal s-access-detail-page  a-text-normal"[^>]*?href=\"([^>]*)\"[^>]*?>'

注意到,由于正则默认为贪婪模式,所以我们可以通过在*****后面增加?来实现非贪婪模式查找。

查找到网页后,记录下当前的网站,依次访问。
然后根据需求,我们要获得一下内容

  • 书名 – 在url中含有
  • 作者名 – 标签下
  • 出版时间 – 标签下,这里注意,由于时间的前方存在一个"-“符号,而这个符号在html中的实际存在方式为”&ndash",所以我们在写正则的时候需要注意
  • 售价 – 标签下
  • 出版社 – 出版社: 下方
  • 丛书名 – 丛书名: 下方(不一定存在)
  • 内容简介 –
    后方
  • ISBN码 – ISBN: 这里的"“不是表示真正的”",只是没有标签的时候的表现方式
    然后这里有个大坑!内容简介里面的东西其实是虚拟节点!也就是一个还没加载的内容。。。然而仔细看,好像这个js脚本直接写在页面里面了,而且连显示的内容也写在了里面。。。

测试了一下,大致是可以完成需要的功能了~
然后是操作excel,虽然vba什么的没看过,但是python有excel的写入库xlwt,于是这里尝试学习一下:

1
2
3
4
5
6
7
8
9
10
11
12
import xlwt
# 创建 xls 文件对象
wb = xlwt.Workbook()
# 新增一个表单
sh = wb.add_sheet('A Test Sheet')
# 按位置添加数据
sh.write(0, 0, 1234.56)
sh.write(1, 0, 8888)
sh.write(2, 0, 'hello')
sh.write(2, 1, 'world')
# 保存文件
wb.save('example.xls')

上网找了的demo,那么对应关系估计就是
|实际内容 |操作对象 |
|excel文件 |workbook
|表单 |sheet
|单元格内容 |write写入
那么我们大致就知道要如何写了:创建一个excel,并且将数据按照指定的位置放入。

----------=========------------

出现了新的需求,需要截图网站上的图片并且存到当前的目录下。这个好像也是可以实现的。然后发现一个麻烦的事情。。。图片嘛,肯定就扯到了编码的问题,然而我好像怎么搞都搞不掂。。因为url抓到的页面大小好像不太对。。。可能是多张图片什么的,反正图片的个数错了。。。于是乎用了别的思路,直接扒了网页里面的图片,也就是base64处理后的那个。。。。虽然可能压缩了,但是应该还是没问题的。。于是就将这个取下来,然后base64处理一下应该就没问题了。

4、具体应用

由于同学提到说自己也想乘机学一下,所以打算写一个依赖关系清单,让他也能快速安装。
装上pipreqs可以快速的生成我们的requirements.txt。然而发现一直在报错UnicodeDecodeError,想到可能是因为windows的cmd编码问题,于是使用参数强制指定编码为utf-8:

1
pipreqs --encoding=utf-8 --use-local ./

那么如果对方需要快速的安装对应库的话,只需要在当前目录下用用

1
pip install -r requirements.txt

命令,即可快速的装上对应的依赖库。

后来想起来要有一个可视化的界面方便操作。。。好吧操起丢了好久的pyqt简单撸一个:
首先用qt designer做一个先。。。

然后利用pyuic5命令生成一个对应的.py文件

1
pyuic5 -o ui_Dialog.py Dialog.ui

最后用另一个程序将这个内容包括进去,使用pyqt对其进行操作。为了让程序运行的时候不发生卡顿,这里使用了QThread对其进行异步处理,并且加上了槽函数:

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
class SpiderCrawl(QThread):
"""Lanch Spider"""
SignalFinishSpdier = pyqtSignal(bool)
def __init__(self, needs, parent=None):
super(SpiderCrawl, self).__init__(parent)
self.needs = needs
self.SignalFinishSpdier.connect(parent.spiderFinish)

def run(self):
""" it will return result of the spider

Ret: the result of spider
"""
result = amazonSpider.Spider(self.needs)
self.SignalFinishSpdier.emit(result)

class SpiderDialog(QDialog, ui_Dialog.Ui_Dialog):
...
@pyqtSlot(bool)
def spiderFinish(self, result):
""" the spider is finish"""
print("There!")
if result == True:
self.KeywordText.setText("爬虫已经完成任务")
else:
self.KeywordText.setText("你的url有问题...")

self.takeButton.setEnabled(True)

最后为了防止手误输入了错误的网址。。。这里还进行了检测,如果无法在当前的网页中找到合适的url的话,也视为输入错误。
最后附上项目的网址:
amazonSpider V1.0