58同城南京品牌公馆数据爬取

  做一个租房信息的网站,要爬取58同城上南京品牌公馆的房源信息,因为数字被重新编码了,折腾了一天,记录一下整个过程,留着后面使用。

1,网页分析和字体文件反爬

  简单看了下url(https://nj.58.com/pinpaigongyu/pn/1/),比较简单,替换下网址中页码数,就可以一直翻页并进行爬取。分析了下源代码需要爬取的信息,如下图所示,有一些怪异的汉字,刚开始以为是编码问题,后来看了下爬取下来的网页,发现是数字被替换了,应该是有特殊的字体文件。

  

  

  在script标签中找了一会,发现了下面的js代码,看到font-face和fangchan-secret,应该就是相应的字体文件,base64后面的内容即为base64加密后的字体文件信息,可以进行正则匹配提取,然后解码写入字体文件,相应代码如下。

import requestsimport refrom fontTools.ttLib import TTFont#下载字体文件url="https://nj.58.com/pinpaigongyu/pn/{page}/"
response = requests.get(url=url.format(page=1),headers={‘User-Agent‘:‘Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1‘})
font_base64 = re.findall("base64,(AA.*AAAA)", response.text)[0]  # 找到base64编码的字体格式文件
font = base64.b64decode(font_base64)
with open(‘58font2.ttf‘, ‘wb‘) as tf:
    tf.write(font)font_object = TTFont(‘58font2.ttf‘)font_object.saveXML(‘58font2.xml‘)

  利用FontCreator查看了下保存的字体文件58font2.ttf,信息如下,共十一个对应表,第一个为空,不起作用,后面分别是数字和对应的字体编码。

  然后看了下58font2.xml的内容,如下所示,结合上面的图,可以发现相应的对应关系如下: uni9476(0x9476)和glyph00008都对应着数字7,即对每一个glyph0000x对应数字x-1  

 因此只需要将下载的网页中的 (麣室龤厅龤卫 )乱码内容替换为相应的数字即可,需要运用到fontTools模块,具体步骤如下:      1,利用fontTools模块解析出对应的编码转换表(convert_dict数据如下:{40611: ‘glyph00004‘, 40804: ‘glyph00009‘, 40869: ‘glyph00010‘......},其中40611即为0x9ea3对应的十进制数)      2,将&#x9ea3等十个字体编码进行拼接,组成一个正则pattern,对下载的网页进行匹配搜索       3,将匹配到的字体编码,通过编码转换表替换成数字。    对应代码如下:
def convertNumber(html_page):
    base_fonts = [‘uni9FA4‘, ‘uni9F92‘, ‘uni9A4B‘, ‘uni9EA3‘, ‘uni993C‘, ‘uni958F‘, ‘uni9FA5‘, ‘uni9476‘, ‘uni9F64‘,
                  ‘uni9E3A‘]
    base_fonts2 = [‘&#x‘ + x[3:].lower() + ‘;‘ for x in base_fonts]  # 构造成 鸺 的形式
    pattern = ‘(‘ + ‘|‘.join(base_fonts2) + ‘)‘  #拼接上面十个字体编码

    font_base64 = re.findall("base64,(AA.*AAAA)", response.text)[0]  # 找到base64编码的字体格式文件
    font = base64.b64decode(font_base64)
    with open(‘58font2.ttf‘, ‘wb‘) as tf:
        tf.write(font)
    onlinefont = TTFont(‘58font2.ttf‘)
    convert_dict = onlinefont[‘cmap‘].tables[0].ttFont.tables[‘cmap‘].tables[0].cmap  # convert_dict数据如下:{40611: ‘glyph00004‘, 40804: ‘glyph00009‘, 40869: ‘glyph00010‘, 39499: ‘glyph00003‘
    new_page = re.sub(pattern, lambda x: getNumber(x.group(),convert_dict), html_page)
    return new_page

def getNumber(g,convert_dict):
    key = int(g[3:7], 16)  # ‘&#x9ea3‘,截取后四位十六进制数字,转换为十进制数,即为上面字典convert_dict中的键
    number = int(convert_dict[key][-2:]) - 1  # glyph00009代表数字8, glyph00008代表数字7,依次类推
    return str(number)

2,爬取网页

  字体文件弄清楚后剩下的内容就很简单了,注意下爬取时两次间隔时间,必须大于5秒,否则会要求手势验证码。另外对于爬虫停止爬取的条件,我设置了匹配不到爬取信息和网页页码数小于600,最后发现实际上只有170页。爬取数据并存入数据库,完整代码如下:

#coding:utf-8

import requests
from lxml import html
import csv
import re
from fontTools.ttLib import TTFont
import base64
import MySQLdb
import time
from download import get_user_agent

def download(url,headers=None,cookies=None,proxies=None,num_retries=3):  #支持user-agent和proxy
    #proxies = {"http": "http://10.10.1.10:3128", "https": "http://10.10.1.10:1080",}
    response=requests.get(url,headers=headers,cookies=cookies,proxies=proxies)
    if response.status_code and 500<=response.status_code<600:  # 出现服务器端错误时重试三次
        if num_retries > 0:
            response = download(url,user_agent,proxies,num_retries-1)
    return response

def convertNumber(html_page):

    base_fonts = [‘uni9FA4‘, ‘uni9F92‘, ‘uni9A4B‘, ‘uni9EA3‘, ‘uni993C‘, ‘uni958F‘, ‘uni9FA5‘, ‘uni9476‘, ‘uni9F64‘,
                  ‘uni9E3A‘]
    base_fonts2 = [‘&#x‘ + x[3:].lower() + ‘;‘ for x in base_fonts]  # 构造成 鸺 的形式
    pattern = ‘(‘ + ‘|‘.join(base_fonts2) + ‘)‘

    font_base64 = re.findall("base64,(AA.*AAAA)", response.text)[0]  # 找到base64编码的字体格式文件
    font = base64.b64decode(font_base64)
    with open(‘58font2.ttf‘, ‘wb‘) as tf:
        tf.write(font)
    onlinefont = TTFont(‘58font2.ttf‘)
    convert_dict = onlinefont[‘cmap‘].tables[0].ttFont.tables[‘cmap‘].tables[0].cmap  # convert_dict数据如下:{40611: ‘glyph00004‘, 40804: ‘glyph00009‘, 40869: ‘glyph00010‘, 39499: ‘glyph00003‘
    new_page = re.sub(pattern, lambda x: getNumber(x.group(),convert_dict), html_page)
    return new_page

def getNumber(g,convert_dict):
    key = int(g[3:7], 16)  # ‘&#x9ea3‘,截取后四位十六进制数字,转换为十进制数,即为上面字典convert_dict中的键
    number = int(convert_dict[key][-2:]) - 1  # glyph00009代表数字8, glyph00008代表数字7,依次类推
    return str(number)

def getLocation(title):
    desc = title.split()
    if u‘寓‘ in desc[0]or u‘社区‘ in desc[0]:
        location = desc[0].strip(u‘【整租】【合租】‘)+desc[2]
    else:
        location=desc[1]
    return location

def getAveragePrice(price):
    price_range = price.split(‘-‘)
    if len(price_range)==2:
        average_price = (int(price_range[0])+int(price_range[1]))/2
    else:
        average_price=price
    return average_price

if __name__ == ‘__main__‘:

    conn = MySQLdb.connect(host=‘localhost‘,user=‘root‘,passwd=‘‘,db=‘renthouse‘,charset=‘utf8‘,port=3306) #不要写成utf-8
    cursor = conn.cursor()
    cursor.execute("""CREATE TABLE IF NOT EXISTS 58house(
                        id INT PRIMARY KEY AUTO_INCREMENT NOT NULL,
                        title VARCHAR(128) NOT NULL,
                        location VARCHAR(128) NOT NULL,
                        room VARCHAR(128) NOT NULL ,
                        price VARCHAR(64) NOT NULL ,
                        average_price VARCHAR(64) NOT NULL ,
                        url VARCHAR(768) NOT NULL);""")
    conn.commit()
    seed_url = "https://nj.58.com/pinpaigongyu/pn/{page}/"
    page = 0
    flag=True
    while flag:
        try:
            user_agent = get_user_agent()
            headers={"User_Agent":user_agent}
            page = page+1
            current_url=seed_url.format(page=page)
            print current_url
            response = download(url=current_url,headers=headers)
            new_page = convertNumber(response.text)
            tree = html.fromstring(new_page)
            li_tags = tree.xpath(‘//ul[@class="list"]/li‘)
            #print li_tags
            if (not li_tags) or page>600:
                print page
                flag=False
            for li_tag in li_tags:
                title = li_tag.xpath(‘.//div[@class="des strongbox"]/h2/text()‘)[0].strip()
                room = li_tag.xpath(‘.//p[@class="room"]/text()‘)[0].replace(‘\r\n‘, ‘‘).replace(r‘ ‘, ‘‘)
                price = li_tag.xpath(‘.//div[@class="money"]//b/text()‘)[0].strip().replace(‘\r\n‘, ‘‘).replace(r‘ ‘, ‘‘)
                url = li_tag.xpath(‘./a[@href]‘)[0].attrib[‘href‘].strip()
                location = getLocation(title)
                average_price = getAveragePrice(price)
                cursor.execute("""INSERT INTO 58house(title,location,room,price,average_price,url)
                        VALUES(‘%s‘,‘%s‘,‘%s‘,‘%s‘,‘%s‘,‘%s‘);"""%(title,location,room,price,average_price,url))
                conn.commit()
        except Exception as e:
            print "download page %s error: %s"%(page,e)
        time.sleep(5) # 停顿时间小于5s时会要求图形验证码
    conn.close()

  十多分钟后就爬取完毕了,总共170页,爬取第171页时失败,数据库中共存入了3398条数据。

 

原文地址:https://www.cnblogs.com/silence-cho/p/10202552.html

时间: 2024-11-09 09:41:06

58同城南京品牌公馆数据爬取的相关文章

爬虫案例—中基协数据爬取

因为工作原因,需要爬取相关网站的数据,包括中基协网站和天眼查部分数据. 一.中基协网站 爬取思路: 1.查看目标页:http://gs.amac.org.cn/amac-infodisc/api/pof/manager?rand=0.9775162173180119&page=%s&size=50 发现有随机数字串(刷新反爬措施),以及页码和每页信息条数,可以用来拼接爬取url 用一个循环爬取所有展示页面,用到requests库以及random函数生成随机数 返回的是json数据,直接用r

XPath之电影天堂数据爬取

2019.5.19,今天写这个实例写了几个小时,虽然不多,100行代码左右,但是貌似是学python以来代码写得最多的一次了. 主要是看了这个视频https://www.bilibili.com/video/av51650499?from=search&seid=10478614069026676839 然后自己尝试写了一下,大部分都能写出来,就是最后获取“主演”和“简介”的时候不太好获取,需要运用到下标并使用循环. 另外,还需要注意的是,我写的这个程序是以电影名称命名然后以json格式存储在本

数据挖掘工具分析北京房价 (一) 数据爬取采集

一. 前言 房价永远是最让人头疼且激动的话题,尤其是在帝都,多少人一辈子都为了一套房子打拼.正好我也想用一个大家比较关心的话题作为案例,把目前我开发的这套软件进行一次完整的演练.从数据采集,到清洗,分析,和最终可视化和报告的呈现,实现一次完整的流程.一方面可以给大家切实的分享一些有用的信息,再者可以更好地了解这套软件的使用流程.  关于本工具的介绍,可参考数据挖掘平台介绍(综述)——平台简介. 自然的,文章分为四部分,本节是第一部分:数据爬取和采集. 二.  准备工作 俗话说巧妇难为无米之炊,没

python实现数据爬取-清洗-持久化存储-数据平台可视化

基于python对淘宝模特个人信息进行筛选爬取,数据清洗,持久化写入mysql数据库.使用django对数据库中的数据信息筛选并生成可视化报表进行分析. 数据爬取,筛选,存库: # -*- coding:utf-8 -*-   import requests from bs4 import BeautifulSoup import sys import re reload(sys) sys.setdefaultencoding('utf-8') import MySQLdb import cha

python实现人人网用户数据爬取及简单分析

这是之前做的一个小项目.这几天刚好整理了一些相关资料,顺便就在这里做一个梳理啦~ 简单来说这个项目实现了,登录人人网并爬取用户数据.并对用户数据进行分析挖掘,终于效果例如以下:1.存储人人网用户数据(户主的全部好友.户主好友的全部好友.户主及好友关注的公共主页).2.信息可视化,绘制户主好友间的关系网络图:3.数据挖掘,通过分析户主好友关注的公共主页,向户主推荐公共主页. 项目分为三个部分,各自是人人网登录及用户数据爬取与存储.用户关系可视化.公共主页推荐.以下分别介绍这三个部分. 第一部分实现

Python爬虫 股票数据爬取

前一篇提到了与股票数据相关的可能几种数据情况,本篇接着上篇,介绍一下多个网页的数据爬取.目标抓取平安银行(000001)从1989年~2017年的全部财务数据. 数据源分析 地址分析 http://money.finance.sina.com.cn/corp/go.php/vFD_FinancialGuideLine/stockid/000001/ctrl/2017/displaytype/4.phtml 在浏览器(PC上)中打开这个地址,就可以看到下图显示的财务数据.这个地址是一个通用格式:(

爬虫1.5-ajax数据爬取

目录 爬虫-ajax数据爬取 1. ajax数据 2. selenium+chromedriver知识准备 3. selenium+chromedriver实战拉勾网爬虫代码 爬虫-ajax数据爬取 1. ajax数据 ajax (异步JavaScript 和 XML)(读作阿贾克斯),ajax可以时网页实现异步更新,一般使用的json数据交互,即在不重新加载整个页面也可以对网页的部分进行更新,ajax技术加载的数据在网页源代码中是看不到的,只能看到url加载的html部分 获取ajax数据的两

Python爬虫入门教程 15-100 石家庄政民互动数据爬取

1. 石家庄政民互动数据爬取-写在前面 今天,咱抓取一个网站,这个网站呢,涉及的内容就是 网友留言和回复,特别简单,但是网站是gov的.网址为http://www.sjz.gov.cn/col/1490066682000/index.html 首先声明,为了学习,绝无恶意抓取信息,不管你信不信,数据我没有长期存储,预计存储到重装操作系统就删除. 2. 石家庄政民互动数据爬取-网页分析 点击更多回复 ,可以查看到相应的数据. 数据量很大14万条,,数据爬完,还可以用来学习数据分析,真是nice 经

基于CrawlSpider全栈数据爬取

CrawlSpider就是爬虫类Spider的一个子类 使用流程 创建一个基于CrawlSpider的一个爬虫文件 :scrapy genspider -t crawl spider_name www.xxx.com 构造链接提取器和规则解析器 链接提取器: 作用:可以根据指定的规则进行指定连接的提取 提取的规则: allow = "正则表达式" 会先在全局匹配所有的url,然后根据参数allow的规则匹配需要的链接 规则解析器 作用:获取链接提取器提取到的链接,对其进行请求发送,根据