需要的工具:

  • Python3
  • requests
  • BeautifulSoup
  • openpyxl
  • re
  • PIL
  • urllib
  • pytesseract

本文两个例子的完整代码:https://github.com/huanyouchen/python-spider

本文目标

爬取网页中表格类的信息,并将这些信息存储到excel中。

实例1

目标URL: http://permit.mep.gov.cn/permitExt/outside/Publicity

全国排污许可证许可信息公开的相关信息

该网站展示的是全国排污许可证管理信息平台-公开端中许可信息公开的相关信息,目标是获取所有公开页面的表格中的省/直辖市、地市、许可证编号、单位名称、行业类别、有效期限、发证日期、查看链接,并将所有信息写入excel中。

实例2

目标URL: http://info.425500.cn/category-1-0-0-0-p1.html

江华地区物品交易平台的交易信息

该网站展示的是江华地区物品交易平台的交易信息,目标是获取每单交易的标题,发布时间,浏览次数,所在区域,联系电话,和交易详细链接,并将所有信息写入excel中。

爬取结果

实例1的结果

全国排污许可证许可信息公开的相关信息爬取结果

实例2的结果

江华地区物品交易平台的交易信息爬取结果

过程分析

完整代码在本文开头已经给出地址,需要自取。下面分析部分内容。

实例1分析

在get_end_page_num()函数中,获取最后一页的页码,需要用正则表达式来提取出第一个数字,也就是2019,表示一共有2019个页面。

1
2
# 获取最后一页页码
end_page_num = re.findall('\d+', end_page_text)[0]

获取表格数据核心代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
trs = soup.find('div', class_='tb-con').find_all('tr')[1:]
# 存储该页表格数据
page_data = []
for tr in trs:
row_data = []
for td in tr:
# 过滤掉'\n'字符和最后一个空的链接内容
if td.string != '\n' and td.string:
row_data.append(td.string)
link = 'http://permit.mep.gov.cn' + \
tr.select('td.bgcolor1 > a')[0]['href']
row_data.append(link)
page_data.append(row_data)
data += page_data
print("读取第%s页数据成功" % page_num)

在表格中,tr代表一行,td代表一列,外层循环获取每一行,内层循环获取每一行的每一列。
row_data存储一行中各列的数据,也就是省/直辖市、地市、许可证编号等,但是最后一列是超链接,需要单独处理一下然后存入到row_data中。
page_data存储的是每页的表格数据,也就是把这一页中的所有行数据row_data存到一起。
最后的data存储所有页page_data的数据。

本文使用openpyxl操作excel。将数据存入excel中:

1
2
3
4
5
6
7
8
9
10
11
12
for i in range(2, len(data) + 2):
# 编号
sheet.cell(row=i, column=1, value=i-1)
for j in range(1, 8):
try:
sheet.cell(row=i, column=j+1, value=data[i-2][j-1])
# 最后一个单元格链接单独处理
sheet.cell(row=i, column=9, value=('=HYPERLINK("%s")' % (data[i-2][7])))
except Exception as e:
print("第" + i + "行", "第" + j + "列数据写入出错")
print(e)
wb.save("全国排污许可证管理信息平台许可公开信息.xlsx") # EXCEL保存

存入excel表格中也需要用两个循环,i表示行递增,j表示每一行的列,将data中的数据依次存入即可。

其中有一个小知识点,爬到的查看链接是URL,需要用把url用超链接的形式写入Excel中,具体用法是在value里面加入=HYPERLINK

1
sheet.cell(row=i, column=9, value=('=HYPERLINK("%s")' % (data[i-2][7])))

实例2分析

实例2比实例1复杂一点,主要有两点。

第一点,获取电话号码,该网站的电话号码不是文本形式,而是一个图片,用Chrome开发者工具查看相关源码如下:

1
2
3
<li>联系方式:<span class="cBlack">
<img align="absmiddle" src="../public/ajax.aspx?action=drawinfoimage&amp;in=8SwlFtKb9u10iAntlNhdM8Vg==&amp;size=16">
</span> </li>

电话图片的地址为: http://info.425500.cn/public/ajax.aspx?action=drawinfoimage&in=8SwlFtKb9u10iAntlNhdM8Vg==&size=16

思路是提取出电话图片的地址url,然后用urllib中的urlretrieve把图片下载到本地,然后使用Tesseract识别图片中的文字,将识别出来的结果返回得到文本形式的电话号码,然后写入excel中。

关于Tesseract的下载和使用: http://huanyouchen.github.io/2018/05/10/install-Tesseract-and-use-it-with-py/

这部分的实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def img_to_str(image_path):
# 识别图片上的电话
return pytesseract.image_to_string(Image.open(image_path))

tel_img_down_path = './telImg/'
if not os.path.exists(tel_img_down_path):
os.mkdir(tel_img_down_path)
tel_img_src = soup.select("div.newscontent2 > div.nc2-content > ul > li > span.cBlack > img")[0].get("src")
tel_img_src_full = "http://info.425500.cn" + tel_img_src[2:]
tel_img_title = title + '.png'
# 将电话图片下载到指定文件夹,用tesseract识别出电话号码
urllib.request.urlretrieve(tel_img_src_full,
os.path.join(tel_img_down_path, tel_img_title))
tel = img_to_str(tel_img_down_path + tel_img_title)

另一个点是对浏览次数的获取。浏览次数相关页面源码如下:

1
2
3
<span class="left news-span2">浏览
<script src="../public/ajax.aspx?action=addnum&amp;id=6581&amp;t=1"></script>114次
</span>

浏览次数使用后台脚本计算后返回给前端页面中的,先用Dev工具中的Network找到这段脚本:

后台累加浏览次数脚本

该脚本地址为:http://info.425500.cn/public/ajax.aspx?action=addnum&id=6576&t=1

其中id为该次交易信息的id号,这个id号在本物品交易详情的url中,比如这个哈士奇出售的交易详情url: http://info.425500.cn/info-6576.html

思路是首先从物品交易详情页面的url中提取出该物品交易id号,将这个id号作为参数传入后台累加浏览次数脚本的url中,用request请求该脚本链接,然后用正则表达式提取出返回信息中的浏览次数。

这部分实现代码:

1
2
3
4
5
info_id = re.findall('\d+', link.split('/')[-1])[0]  # 通过网址获取交易编号
# 交易信息浏览次数是通过后台脚本处理的:http://info.425500.cn/public/ajax.aspx?action=addnum&id=交易编号&t=1
info_id_add_url = 'http://info.425500.cn/public/ajax.aspx?action=addnum&id=%s&t=1' % info_id
view_num = requests.get(info_id_add_url).text
view_num = "浏览了" + re.findall('\d+', view_num)[0] + "次" # 正则取出交易浏览次数