[Python]写给Dr.Wu的简单爬虫例子

概览

这次要爬的数据来自网站:http://www.qlaee.com/zhuanlist.jsp?flag=3&p=1&columnumber=302&codemyid=qlpreweb21

界面大概是这样的:

需要的数据就是图中的表格,表格的每一项都还有详情页面,如下图:

处理第一个页面

上图表格可以看到,这个数据共有8页,对于多页的处理,需要找到其页面的网址,然后通过页面网址的序号进行多页面的遍历。

我们把鼠标右击下一页,复制下一页的网址,看到如下:

http://www.qlaee.com/news_list3.jsp?p=2&columnumber=302&countyid=&outareat=&areaunit=&cityid=&baseprice=&codemyid=qlpreweb21&outareaf=

其实也可以再获取下一页的网址,对比,发现变化的是p=2,所以对这8页的处理逻辑为

for i in range(1, 9):
    url = "http://www.qlaee.com/news_list3.jsp?p=" + str(i) \
    + "&columnumber=302&countyid=&outareat=&areaunit=&cityid=&baseprice=&codemyid=qlpreweb21&outareaf="

得到每个页面的网址,就可以用requests进行页面爬取。

html = requests.get(url).content.decode('utf-8')

爬到的页面部分关键数据如下:

<div class="list-news">
    <table border="0" cellpadding="0" cellspacing="0" width="100%" style="border-left: 1px solid #DEDEDE; border-right: 1px solid #DEDEDE">
        <tr class="x_hei">
            <td bgcolor="#A6C7EE" height="24" align="center" width="160">项目编号</td>
            <td bgcolor="#A6C7EE" align="center" width="320">项目名称</td>
            <td align="center" bgcolor="#A6C7EE" width="140">挂牌价格</td>
            <td align="center" bgcolor="#A6C7EE" width="100">挂牌时间</td>
            <td align="center" bgcolor="#A6C7EE" width="100">交易状态</td>
        </tr>
        <tr class="x_hei">
            <td bgcolor="#E4EEFA" height="24" align="center" width="160"><a href="zhuanr_info.jsp?proid=1ce451df-6eb0-11e8-ab33-0894ef1b2f8f&columnumber=315&codemyid=qlpreweb21&parentid=302" target="_blank">3707020101201806130003</a> </td>
            <td bgcolor="#E4EEFA" align="left" width="320"> <a href="zhuanr_info.jsp?proid=1ce451df-6eb0-11e8-ab33-0894ef1b2f8f&columnumber=315&codemyid=qlpreweb21&parentid=302" target="_blank">潍坊市潍城区于河出租土地4.8亩</a></td>
            <td align="center" bgcolor="#E4EEFA" width="140">1100元/亩/年</td>
            <td align="center" bgcolor="#E4EEFA" width="100">2018-06-13</td>
            <td width="98" align="center" bgcolor="#E4EEFA">
                <span lang="en-us">挂牌中</span>
            </td>
        </tr>
        <tr class="x_hei">
            <td bgcolor="#E4EEFA" height="24" align="center" width="160"><a href="zhuanr_info.jsp?proid=958f33b5-6ead-11e8-ab33-0894ef1b2f8f&columnumber=315&codemyid=qlpreweb21&parentid=302" target="_blank">3707020101201806130002</a> </td>
            <td bgcolor="#E4EEFA" align="left" width="320"> <a href="zhuanr_info.jsp?proid=958f33b5-6ead-11e8-ab33-0894ef1b2f8f&columnumber=315&codemyid=qlpreweb21&parentid=302" target="_blank">潍坊市潍城区于河出租土地4.8亩</a></td>
            <td align="center" bgcolor="#E4EEFA" width="140">1200元/亩/年</td>
            <td align="center" bgcolor="#E4EEFA" width="100">2018-06-13</td>
            <td width="98" align="center" bgcolor="#E4EEFA">
                <span lang="en-us">挂牌中</span>
            </td>
        </tr>

拿到源码数据,就需要使用 BeautifulSoup 对源码进行解析。不过解析前需要分析一下代码结构。

表格数据是标准的 <tr><td> 标签,但因为界面上还有其他的表格,所以不能直接过滤 <tr><td> 。通过对比,我们需要的每一行数据包裹在 <tr class="x_hei"> ,所以过滤 x_hei 即可。

soup = BeautifulSoup(html, 'html.parser')
trs = soup.find_all('tr', attrs={'class': 'x_hei'})

拿到所有的 <tr> ,即表格的每一行。 <trs> 是个 list ,先看下标题数据:

[<td align="center" bgcolor="#A6C7EE" height="24" width="160">项目编号</td>, 
<td align="center" bgcolor="#A6C7EE" width="320">项目名称</td>, 
<td align="center" bgcolor="#A6C7EE" width="140">挂牌价格</td>, 
<td align="center" bgcolor="#A6C7EE" width="100">挂牌时间</td>, 
<td align="center" bgcolor="#A6C7EE" width="100">交易状态</td>]

遍历该list,提取每一行的文字内容即可。对文字的提取,可以看到规律,文字包裹在 > </中,所以用正则表达式就可以轻松拿到想要的数据。

for index, item in enumerate(td):
    item = str(item)
    pattern = re.compile(r'>(.*?)</', re.S | re.M)
    text = re.findall(pattern, item)
    tmp_list.append(text[0])

然后看下具体内容数据:

[<td align="center" bgcolor="#E4EEFA" height="24" width="160">
 <a href="zhuanr_info.jsp?proid=1ce451df-6eb0-11e8-ab33-0894ef1b2f8f&amp;columnumber=315&amp;codemyid=qlpreweb21&amp;parentid=302" target="_blank">3707020101201806130003</a> </td>, 
 
 <td align="left" bgcolor="#E4EEFA" width="320"> <a href="zhuanr_info.jsp?proid=1ce451df-6eb0-11e8-ab33-0894ef1b2f8f&amp;columnumber=315&amp;codemyid=qlpreweb21&amp;parentid=302" target="_blank">潍坊市潍城区于河出租土地4.8亩</a></td>, 
 
 <td align="center" bgcolor="#E4EEFA" width="140">1100元/亩/年</td>, 
 
 <td align="center" bgcolor="#E4EEFA" width="100">2018-06-13</td>, 
 
 <td align="center" bgcolor="#E4EEFA" width="98"><span lang="en-us">挂牌中</span></td>]

和标题略有一些出入,内容前两项包裹在 <a> 中,即有着对应内容的网址链接,中间两项在 <td>中,最后一项在一个<span>中。

所以需要另外处理这些内容

if index == 0:
    result = re.findall(r'<a.*?>(.*?)</a>', item, re.S | re.M)
    link = re.findall(r'<a.*?href="(.*?)"', item, re.S | re.M)
    web = "http://www.qlaee.com/" + link[0]
    web_list.append(web)
elif index == 1:
    result = re.findall(r'<a.*?>(.*?)</a>', item, re.S | re.M)
elif index == 2:
    result = re.findall(r'<td.*?>(.*?)</td>', item, re.S | re.M)
elif index == 3:
    result = re.findall(r'<td.*?>(.*?)</td>', item, re.S | re.M)
elif index == 4:
    result = re.findall(r'<span.*?>(.*?)</span>', item, re.S | re.M)

这里注意,使用一个全局的web_list保存网址链接,用作下一步的页面爬取。

到这里,这个页面的数据就爬取完成。

详情页数据提取

依然先用requests获取页面数据,然后使用同样的方法处理数据。这里主要提一下差异:

通过提取源码,知道这个页面表格被 <div> 包裹:

<!-- 详细信息 -->
<div class="content-list1">
    <h3 class="no-line">潍坊市潍城区于河出租土地4.8亩</h3>
    <div class="info-content">
        <table width="100%" border="0" align="center" cellpadding="0" cellspacing="1">
            <tr>
                <th width="28%" height="24" align="right" bgcolor="#E4EEFA" >项目编号&nbsp;&nbsp;</th>
                <td width="32%" >3707020101201806130003</td>
                <th width="20%" align="right" bgcolor="#E4EEFA" >转让方名称&nbsp;&nbsp;</th>
                <td width="20%" >庄仕宏</td>
            </tr>
            <tr>
                <th height="24" align="right" bgcolor="#E4EEFA" >土地性质&nbsp;&nbsp;</th>
                <td >耕地</td>
                <th align="right" bgcolor="#E4EEFA" ><span style="letter-spacing: 2em;margin-right: -2em;">其他</span>&nbsp;&nbsp;</th>
                <td ></td>
            </tr>
            <tr>
                <th height="24" align="right" bgcolor="#E4EEFA" ><span style="letter-spacing: 2em;margin-right: -2em;">坐落</span>&nbsp;&nbsp;</th>
                <td colspan="3" >潍坊市&nbsp;潍城区&nbsp;于河[乡、镇(街道)]&nbsp;南庄[村(居)]</td>
            </tr>
            <!-- 地块信息 -->

            <tr>
                <th height="24" align="right" bgcolor="#E4EEFA" >地块编号&nbsp;&nbsp;</th>
                <td >nzjzzh100</td>
                <th align="right" bgcolor="#E4EEFA" ><span style="letter-spacing: 2em;margin-right: -2em;">用途</span>&nbsp;&nbsp;</th>
                <td ></td>
            </tr>
            <tr>
                <th height="24" align="right" bgcolor="#E4EEFA" >四至情况&nbsp;&nbsp;</th>
                <td  colspan="3">庄仕峰|斗九路|斗八路|斗九路</td>
            </tr>

            <!-- end -->
            <tr>
                <th height="24" align="right" bgcolor="#E4EEFA" >拟转出面积&nbsp;&nbsp;</th>
                <td >4.8亩</td>
                <th align="right" bgcolor="#E4EEFA" >拟转出年限&nbsp;&nbsp;</th>
                <td >6年</td>
            </tr>
            <tr>
                <th height="24" align="right" bgcolor="#E4EEFA" >拟转出方式&nbsp;&nbsp;</th>
                <td>出租</td>
                <th align="right" bgcolor="#E4EEFA" >挂牌时间&nbsp;&nbsp;</th>
                <td>2018-06-13</td>
            </tr>
            <tr>
                <th height="24" align="right" bgcolor="#E4EEFA" >是否属再次转出&nbsp;&nbsp;</th>

                <td >否</td>


                <th align="right" bgcolor="#E4EEFA" >交易方式&nbsp;&nbsp;</th>
                <td >协议</td>
            </tr>
            <tr>
                <th height="24" align="right" bgcolor="#E4EEFA" >标的是否存在抵押、查封等情况&nbsp;&nbsp;</th>

                <td >否</td>


                <th align="right" bgcolor="#E4EEFA" >挂牌价格&nbsp;&nbsp;</th>
                <td >1100元/亩/年</td>
            </tr>
            <tr>
                <th height="24" align="right" bgcolor="#E4EEFA" >中心联系方式&nbsp;&nbsp;</th>
                <td ></td>
                <th align="right" bgcolor="#E4EEFA" >项目联系方式&nbsp;&nbsp;</th>
                <td ></td>
            </tr>
            <tr>
                <th height="24" align="right" bgcolor="#E4EEFA" >结算方式&nbsp;&nbsp;</th>
                <td  colspan="3">分期支付</td>
            </tr>

            <tr>
                <th height="48" align="right" valign="top" bgcolor="#E4EEFA" ><span style="letter-spacing: 2em;margin-right: -2em;">备注</span>&nbsp;&nbsp;</th>
                <td colspan="3" valign="top" >交通便利,水电通畅,地块平整</td>
            </tr>
        </table>
    </div>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>

所以先拿到对应的 <div>

div = soup.find_all('div', attrs={'class': 'content-list1'})[0]

再看其内部数据

  • 主标题,被 <h3> 包裹 <h3 class="no-line">潍坊市潍城区于河出租土地4.8亩</h3>
  • 数据标题,被 <th> 包裹 <th width="28%" height="24" align="right" bgcolor="#E4EEFA" >项目编号&nbsp;&nbsp;</th>
  • 数据内容,被 <td> 包裹 <td width="32%" >3707020101201806130003</td>
  • 特殊项,被 <span> 包裹 <th height="48" align="right" valign="top" bgcolor="#E4EEFA" ><span style="letter-spacing: 2em;margin-right: -2em;">备注</span>&nbsp;&nbsp;</th>

知道这些,就可以按照观察到的进行分别处理,先把标题全部取出来存到一个list中,再把数据全部取数来按顺序存到另一个list中即可。

def get_detail(iurl):
    print("handling url:" + iurl)
    html = requests.get(iurl, timeout=10).content.decode('utf-8')
    soup = BeautifulSoup(html, 'html.parser')
    # print(soup)
    div = soup.find_all('div', attrs={'class': 'content-list1'})[0]

    # for title
    if len(list2) == 0:
        title_list = []
        title_list.append("项目名称")
        ths = div.find_all('th')
        for th in ths:
            th = str(th)
            text = re.findall(r'<span.*?>(.*?)</span>', th, re.S | re.M)
            if len(text) is 0:
                text = re.findall(r'<th.*?>(.*?)</th>', th, re.S | re.M)
            result = text[0].strip()
            title_list.append(result)
        list2.append(handle_list(title_list))

    # for content
    tmp_list = []
    title = div.find_all('h3')
    pattern = re.compile(r'>(.*?)</', re.S | re.M)
    text = re.findall(pattern, str(title))
    tmp_list.append(text[0])

    tds = div.find_all('td')
    for td in tds:
        td = str(td)
        text = re.findall(r'<td.*?>(.*?)</td>', td, re.S | re.M)
        if len(text) is 0:
            text = [""]
        result = text[0].strip()
        tmp_list.append(result)
    list2.append(handle_list(tmp_list))

数据保存

前面的数据都存在一个list中,最后的展示,当然是存在excel中比较好,所以建议使用openpyxl,因为支持xlsx格式。

def save_list_to_excel(listX, name):
    file_path = '/Users/wow/Desktop/spider.xlsx'
    if os.path.exists(file_path):
        wb = openpyxl.load_workbook(file_path)
    else:
        wb = openpyxl.Workbook()
    ws = wb.create_sheet(title=name)

    for index_x, row in enumerate(listX):
        for index_y, col in enumerate(row):
            ws.cell(index_x + 1, index_y + 1, col)
    wb.save(filename=file_path)

使用很简单,先判断文件是否存在,不存在新建表格,存在打开表格,在原来基础上按顺序添加新内容即可。

完整代码

整体的代码很简单。为了代码易读,所以都用的最基本的语法。后面会增加一些多进程处理页面,也会对页面处理方法进行封装,尽量适配更多的相似页面。

# coding: utf-8
import requests
from bs4 import BeautifulSoup
import re
import openpyxl
import os

list1 = []
web_list = []
list2 = []


def fetch_page1(index, item):
    global web_list
    if index == 0:
        result = re.findall(r'<a.*?>(.*?)</a>', item, re.S | re.M)
        link = re.findall(r'<a.*?href="(.*?)"', item, re.S | re.M)
        web = "http://www.qlaee.com/" + link[0]
        web_list.append(web)
    elif index == 1:
        result = re.findall(r'<a.*?>(.*?)</a>', item, re.S | re.M)
    elif index == 2:
        result = re.findall(r'<td.*?>(.*?)</td>', item, re.S | re.M)
    elif index == 3:
        result = re.findall(r'<td.*?>(.*?)</td>', item, re.S | re.M)
    elif index == 4:
        result = re.findall(r'<span.*?>(.*?)</span>', item, re.S | re.M)
    else:
        result = []
    if len(result) > 0:
        return result[0]
    else:
        return ""


def get_page():
    global list1
    get_title = True
    for i in range(1, 2):
        print("handling page " + str(i))
        # url = "http://www.qlaee.com/chengjlist.jsp?p=" + str(i) \
        #       + "&columnumber=-1&codemyid=qlpreweb23"

        url = "http://www.qlaee.com/news_list3.jsp?p=" + str(i) \
              + "&columnumber=302&countyid=&outareat=&areaunit=&cityid=&baseprice=&codemyid=qlpreweb21&outareaf="
        html = requests.get(url).content.decode('utf-8')
        soup = BeautifulSoup(html, 'html.parser')
        trs = soup.find_all('tr', attrs={'class': 'x_hei'})
        if get_title is False:
            trs.pop(0)
        for tr in trs:
            tmp_list = []
            td = tr.find_all('td')
            print(td)
            if len(td) < 0:
                continue
            for index, item in enumerate(td):
                item = str(item)
                if get_title is True:
                    pattern = re.compile(r'>(.*?)</', re.S | re.M)
                    text = re.findall(pattern, item)
                    tmp_list.append(text[0])
                else:
                    tmp_list.append(fetch_page1(index, item))
            get_title = False
            list1.append(tmp_list)
    save_list_to_excel(list1, "info2")


def get_detail(iurl):
    print("handling url:" + iurl)
    html = requests.get(iurl, timeout=10).content.decode('utf-8')
    soup = BeautifulSoup(html, 'html.parser')
    # print(soup)
    div = soup.find_all('div', attrs={'class': 'content-list1'})[0]

    # for title
    if len(list2) == 0:
        title_list = []
        title_list.append("项目名称")
        ths = div.find_all('th')
        for th in ths:
            th = str(th)
            text = re.findall(r'<span.*?>(.*?)</span>', th, re.S | re.M)
            if len(text) is 0:
                text = re.findall(r'<th.*?>(.*?)</th>', th, re.S | re.M)
            result = text[0].strip()
            title_list.append(result)
        list2.append(handle_list(title_list))

    # for content
    tmp_list = []
    title = div.find_all('h3')
    pattern = re.compile(r'>(.*?)</', re.S | re.M)
    text = re.findall(pattern, str(title))
    tmp_list.append(text[0])

    tds = div.find_all('td')
    for td in tds:
        td = str(td)
        text = re.findall(r'<td.*?>(.*?)</td>', td, re.S | re.M)
        if len(text) is 0:
            text = [""]
        result = text[0].strip()
        tmp_list.append(result)
    list2.append(handle_list(tmp_list))


def save_list_to_excel(listX, name):
    file_path = '/Users/wow/Desktop/spider.xlsx'
    if os.path.exists(file_path):
        wb = openpyxl.load_workbook(file_path)
    else:
        wb = openpyxl.Workbook()
    ws = wb.create_sheet(title=name)

    for index_x, row in enumerate(listX):
        for index_y, col in enumerate(row):
            ws.cell(index_x + 1, index_y + 1, col)
    wb.save(filename=file_path)


def handle_list(list_ori):
    list_end = list_ori[-12:]
    list_start = list_ori[0:-12]
    for i, item in enumerate(list_end):
        list_start.insert(6 + i, item)
    return list_start


if __name__ == "__main__":
    get_page()
    if len(web_list) > 0:
        for index, url in enumerate(web_list):
            get_detail(url)
        save_list_to_excel(list2, "detail")

最后恭喜阿根廷小组赛突围

现在阿根廷中后场整体不行,进世界杯都磕磕绊绊,后面淘汰赛估计也够呛了。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏申龙斌的程序人生

零基础学编程030:像黑客般玩玩字符艺术

经常安装盗版软件的朋友可能会看到一些由字母组成的奇怪图案,比如下面这张图: ? (图片来自于网络) 在Python里,我们无法用字母拼出如此复杂的图案,但也可以...

3896
来自专栏iOS开发笔记

iOS开发照片框架详解(一)-- AssetsLibrary

1 概要 在 iOS 设备中,照片和视频是相当重要的一部分。最近刚好在制作一个自定义的 iOS 图片选择器,顺便整理一下 iOS 中对照片框架的使用方法。在 ...

4297
来自专栏李蔚蓬的专栏

10.1.5 布局优化利器之 Hierarchy Viewer

无论是哪本讲解布局优化的参考书,它们都不得不提到Hierarchy Viewer。不过,通常情况下,Hierarchy( 英['haɪərɑːkɪ])Viewe...

873
来自专栏互联网杂技

20个为前端开发者准备的文档和指南8

1.Meteor: The Official Guide(Meteor官方指南) Meteor介绍的链接地址: http://www.geekpark.net/...

3725
来自专栏web前端教室

[web零基础课]~记一个自定义checkBox标签的进化过程&&电商项目作业检查--张潇x

继昨天讲完了jq插件的二种编写方法和区别之后,今天的web前端零基础课中,又回过头来继续把电商网站中的checkBox复选框,进行了进一步的封装与自定义。 //...

2156
来自专栏Nian糕的私人厨房

CSS 字体图标引用

在自己的项目中,我们或多或少的需要插入一些 icon,缺少 UI 设计天赋的我只能去搜寻现有的 icon 了,在这里推荐大家去 Iconfont 阿里巴巴矢量图...

1033
来自专栏顶级程序员

写一个网页进度 loading

来自:简书 作者:jack_lo 原文:www.jianshu.com/p/4c93f5bd9861 loading随处可见,比如一个app经常会有下拉...

5459
来自专栏前端架构与工程

《微信小程序七日谈》- 第三天:玩转Page组件的生命周期

《微信小程序七日谈》系列文章: 本系列的文章并非初学教程,而是笔者在具体开发过程中遇到的问题以及部分解决方案。 前两篇文章第一天:人生若只如初见和第二天:你可...

23310
来自专栏极乐技术社区

微信小程序 wx.request 的封装

自学转行到前端也已近两年,也算是简书和掘金的忠实粉丝,但是以前一直惜字如金(实在是胆子小,水平又低),现在我决定视金钱如粪土(就只是脸皮厚了,水平就那样),好了...

6468
来自专栏腾讯NEXT学位

VScode编辑器神插件!让你入门前端轻松打怪升级!

5354

扫码关注云+社区

领取腾讯云代金券