0%

0. 爬取范围

  • 全国城市

1. 程序构成

  1. URL管理器(模块)
  2. HTML解析器(模块)
  3. HTML下载器(模块)
  4. 爬虫调度器(主程序)
  5. 存储器(模块)

1.1 获取数据栏目(最终实现)

  • 标题
  • 价格
  • 位置
  • 评分
  • 推荐标签
  • 支付标签
  • 简要描述

2. 获取数据方式

采集示例网址:https://www.tujia.com/detail/220623.htm

采集信息项目

对应的xpath

示例

标题

string(//div[@class='room-info']/h1)

星湖街地铁2号线月亮湾站星湖公馆一室

价格

后测试,无法通过xpath获取

358

评分

string(//div[@class='results-comments']/span[@class='hotel-value'])

5

标签

normalize-space(string(//ul[@class='pay-tag']))

免押金 通过信用认证,达标后即可凭信用免押入住支持信用免押的房屋

描述

normalize-space(string(//div[@class='unit-cont intro-cont line-dashed']/div[@class='content-box']/div[@class='desc-info']))

位于苏州独墅湖科教创新区月亮湾创苑路北、星湖街西,直通地铁2号线····

房屋编号

后测试无法获取

(房屋编号:220623)

位置

string(//div[@class='district-info']/span)

月亮湾路星湖公馆

推荐标签

//div[@class='hotel-advan-tag']/ul/li/span/text()

酒店式公寓,1室1卫,1张床,宜住2人,独立卫浴

其他推荐

无法通过xpath直接获取

https://www.tujia.com/detail/326278.htm,….

计划赶不上变化,发现房屋详情页面是通过动态加载的方式载入 价格通过再次发送请求的方式获取。

3. 直接贴代码

3.1 爬虫调度器代码

#-*- encoding:utf-8 -*-
import sys
reload(sys)
sys.setdefaultencoding("utf-8")
import URL_Manage
import HTML_Get_info
import HTML_Download
import time
import Save_Data_to_File
class SMan(object):
    def __init__(self):
        self.Manage=URL_Manage.UrlManage()
        self.Download=HTML_Download.HTMLDownloader()
        self.parser=HTML_Get_info.HTML_get_info()
        self.output=Save_Data_to_File.DataOutPut()
    def run(self,rooturl,page):
        self.Manage.add_new_url(rooturl)
        for i in range(1,page):
            new="https://www.tujia.com/gongyu/suzhou/%d/" %i
            self.Manage.add_new_url(new)
            print new
        self.output.create_csv()
        while(self.Manage.has_new_urls() and self.Manage.old_urls_size()<200000):
            try:
                new_url=self.Manage.get_new_url()
                if 'detail' in new_url:
                    html=self.Download.download(new_url)
                    date=self.parser.get_detial(html)
                    self.output.output(date)
                    print "详情"
                else:
                    html=self.Download.download(new_url)
                    new_urls=self.parser.parser_the_list(html)
                    self.Manage.add_new_urls(new_urls)
                    time.sleep(10)

                print "已经抓取了%s" %self.Manage.old_urls_size()
            except:
                print "run失败"
if __name__=="__main__":
    sman=SMan()
    sman.run("https://www.tujia.com/gongyu/suzhou/",465)

3.2 URL管理器

class UrlManage(object):
    def __init__(self):
        self.new_urls=set()
        self.old_urls=set()
    def new_urls_size(self):
        return len(self.new_urls)
    def old_urls_size(self):
        return len(self.old_urls)
    def has_new_urls(self):
        return self.new_urls_size()!=0
    def get_new_url(self):
        new_url=self.new_urls.pop()
        self.old_urls.add(new_url)
        return new_url
    def add_new_url(self,url):
        if url is None:
            return
        if url  not in self.new_urls and url not in self.old_urls:
            self.new_urls.add(url)

    def add_new_urls(self,urls):
        if urls is None or len(urls)==0:
            return
        for url in urls:
            self.new_urls.add(url)

3.3 HTML下载器代码

import urllib2
import urllib
import read_ips
import random
class HTMLDownloader(object):
    def download(self,url):
        if url is None:
            return None
        l_ips=read_ips.read_ips("o-ips.txt")
        print l_ips
        httpProxyip=random.choice(l_ips)
        httpproxy_hander=urllib2.ProxyHandler(httpProxyip)
        opener=urllib2.build_opener(httpproxy_hander)
        user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:59.0) Gecko/20100101 Firefox/59.0"
        header={'User-Agent':user_agent}
        req=urllib2.Request(url=url,headers=header)
        r=opener.open(req)
        if r.code==200:
            return r.read()
        return None

3.4 存储器代码

#-*- encoding:utf-8 -*-
import sys
reload(sys)
sys.setdefaultencoding("utf-8")
import pandas as pd
import numpy as np

class DataOutPut(object):
    def __init__(self):
        data=[]
    def output(self,data):
        if data is None:
            return
        names=["标题","价格","位置","评分","房屋编号","推荐标签","支付标签","简要描述"]
        da=[[data["title"],data["price"],data["location"],data["score"],data["house_id"],data["adv_tar"],data["paytag"],data["bewrite"]]]
        df=pd.DataFrame(data=da,columns=names)
        df.to_csv("tujia.csv",columns=None,index=None,header=False,encoding="gbk",mode="a+")
    def create_csv(self):
        names = ["标题", "价格", "位置", "评分", "房屋编号", "推荐标签", "支付标签", "简要描述"]
        # da = [[data["title"], data["price"], data["location"], data["score"], data["house_id"], data["adv_tar"],
        #        data["paytag"], data["bewrite"]]]
        df = pd.DataFrame(columns=names)
        df.to_csv("tujia.csv", columns=names, index=None, encoding="gbk",mode="w")

3.5 HTML解析器代码

from lxml import etree
import urllib2
import urllib
import json
import requests
class HTML_get_info(object):
    def get_detial(self,content):
        if content is None:
            return
        html=etree.HTML(content)
        data={}
        data['title']=self.get_element_by_xpath(html=html,xpa="string(//div[@class='room-info']/h1)")
        data['price']=self.get_price(content=html)
        data['score']=self.get_element_by_xpath(html=html,xpa="string(//div[@class='results-comments']/span[@class='hotel-value'])")
        data['paytag']=self.get_element_by_xpath(html=html,xpa="normalize-space(string(//ul[@class='pay-tag']))")
        data['bewrite']=self.get_element_by_xpath(html=html,xpa="normalize-space(string(//div[@class='unit-cont intro-cont line-dashed']/div[@class='content-box']/div[@class='desc-info']))")
        data['house_id']=self.get_element_by_xpath(html=html,xpa="string(//div[@class='unitCheckinTips']/div/span)")
        data['location']=self.get_element_by_xpath(html=html,xpa="string(//div[@class='district-info']/span)")
        data['adv_tar']=self.get_element_by_xpath(html=html,xpa="//div[@class='hotel-advan-tag']/ul/li/span/text()")
        if len(data["score"])<1:
            data["score"]=0.0
        return data

    def get_element_by_xpath(self,html,xpa):
        return html.xpath(xpa)

    def parser_the_list(self,content):
        html=etree.HTML(content)
        next_urls=self.get_element_by_xpath(html=html,xpa="//h3/a[contains(@class,'detail')]/@href")
        #print next_urls
        return next_urls

    def get_other(self,content):
        unitid=self.get_element_by_xpath(html=content,xpa="//p[@class='link-btn-cont']/@unitid")
        hotelid=self.get_element_by_xpath(html=content,xpa="//p[@class='link-btn-cont']/@hotelid")
        if unitid is None and hotelid is None:
            return
        data='{"hotelId":%d,"unitId":%d}'%(int(hotelid[0]),int(unitid[0]))
        print data

        url="https://www.tujia.com/bingo/pc/unit/getOtherUnits"

        header = {'Accept': 'application/json, text/plain, */*', 'Cache-Control': 'max-age=0', 'Connection': 'keep-alive',
             'Content-Length': 33, 'Content-Type': 'application/json;charset=utf-8',
             'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:45.0) Gecko/20100101 Firefox/45.0'}

        req=urllib2.Request(url=url,data=data,headers=header)
        r=urllib2.urlopen(req)
        # print r.read()
        the_json=r.read()
        data_json=json.loads(the_json)
        print data_json["data"]
        Urls=data_json["data"]["hotelUnitsData"]["hotelUnits"]
        print Urls
        next_urls=[]
        for u in Urls:
            next_urls.append(u["unitDetailLink"])
            #print u["unitDetailLink"]
        return next_urls

    def get_price(self,content):
        unitid = self.get_element_by_xpath(html=content, xpa="//p[@class='link-btn-cont']/@unitid")
        hotelid = self.get_element_by_xpath(html=content, xpa="//p[@class='link-btn-cont']/@hotelid")
        if unitid is None and hotelid is None:
            return
        data = '{"checkInDate":"2018-02-09","checkOutDate":"2018-02-10","unitId":"%s","activityInfo":null,"callCenter":false}'%int(unitid[0])
        url = "https://www.tujia.com/bingo/pc/product/getProducts"
       # data = '{"checkInDate":"2018-02-09","checkOutDate":"2018-02-10","unitId":"40871","activityInfo":null,"callCenter":false}'

        header = {'Accept': 'application/json, text/plain, */*', 'Cache-Control': 'max-age=0',
                  'Connection': 'keep-alive',
                   'Content-Type': 'application/json;charset=utf-8',
                  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:45.0) Gecko/20100101 Firefox/45.0'}
        req = urllib2.Request(url=url, data=data, headers=header)
        r = urllib2.urlopen(req)
        the_json = r.read()
        if len(the_json)==128:
            return
        data_json = json.loads(the_json, encoding="utf-8")
        price=data_json["data"]["products"][0]["productPrice"]
        print price
        return price

注:在这些模块里面,解析器的修改次数尤其多。HTML下载器使用的代理为随机从读取的代理ip列表中读取

附:

1. 位于下载器中的代理读取模块代码

import os
import sys
reload(sys)
sys.setdefaultencoding("utf-8")

def read_ips(file_name):
    l_ips=[]
    N_ips=[]
    f=open(file_name,"r")
    l_ips=f.readlines()
    for ip in l_ips:
        N_ips.append({'http': '%s'%ip.replace("\n","").replace("\r","")})
    print N_ips
    print "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
    return N_ips

2. 代理读取器读取的代理文件存储示例

118.114.77.47:8080
219.149.46.151:3129
221.231.109.40:3128

最终成果

  1. 途家网苏州地区公寓数据(2-9)下载 数据量:6876条
  2. 途家网上海地区公寓数据(2-10)下载 数据量:8602条

说明

  • 以上代码100%手敲。
  • 替换爬虫调度器中的连接即可抓取相关城市(所传的数字为城市公寓页面的最后一页的页码数)
  • 租房价格均为基础价格(get_price()方法内部的data,已经被我写死收集的日期为2-9到2-10)
  • 评论什么的都没有抓取(评论需要通过接口POST方法发送CSV到途家的服务器)
  • 关于提供下载的数据,供以后数据分析使用。
  • 效率问题,目前没有使用多线程、多进程,1分钟大约45条。
  • 会出现已经成功获取价格,但是在某个环节出现问题,直接报异常,提示run失败的情况。

答题竞赛网站项目

任务汇总

项目包含以下页面

  1. 登录、注册页面
  2. 答题页面 2.1 展示题目 2.2 判断正误 2.3 对应的动画效果 2.4 生成本局对应的积分信息以及正误情况
  3. 商场页面 3.0 个人积分总和(可用部分) 3.1 展示商品 3.2 商品简介 3.3 商品购买 3.3.1 购物车展示 3.3.2 购物差价格计算
  4. 闯关页面 4.1 显示当前关卡 4.2 显示总关卡 4.3 解锁新关卡的提示动画
  5. 用户信息页面(个人中心) 5.1 显示注册时间 5.2 显示昵称 5.3 显示正确率 5.4 拥有的积分数
  6. 积分页面 6.1 积分获取\消费记录
  7. 后台页面(管理员) 7.1 当前服务器答题正确数(累计总数)排名 7.2 注册用户数 7.3 题库题数显示 7.4 题库题目导入 7.5 已导入的题目逐条修改 7.6 支持删除已导入的题目 7.7 重置用户密码 7.8 修改、删除用户信息(包括积分修改,昵称修改) 7.9 商场道具(新增,删除,修改) 7.10 修改登录页轮播图 7.11 新增,修改,删除用户状态

功能实现

  1. 注册功能
  2. 登录功能
  3. 答题功能(答题,回退上一题,修改答案,倒计时)
  4. 题库管理功能(增删查改)
  5. 修改登录轮播图功能(管理员后台功能)
  6. 修改用户信息(管理员后台功能)
  7. 积分记录(用户界面以及管理员界面)
  8. 调整积分功能(管理员界面)
  9. 导入题库功能
  10. 积分后台查询
  11. 商城积分消费记录(可查)
  12. 商城购买道具(用户)
  13. 道具使用(涉及到答题界面的道具显示,与商城内道具的购买)
  14. 道具效果(需在答题界面使用js实现相关功能)
  15. 全服正确回答次数排名(累计正确次数)
  16. 关卡的解锁判定(是否解锁下一关)
  17. 用户状态管理(禁止登录、准许登录)

网站安全方面

  1. 普通用户无法访问管理员界面(通过过滤器实现)
  2. 普通用户无法直接提交HTML代码(JS也不行)【都要用HTML编码后储存】

利用yagmail发送电子邮件

1. 安装yagmail

pip install yagmail

2. 使用yagmail

#-*- encoding:utf-8 -*-
import yagmail
yag=yagmail.SMTP(user="xxxxx@126.com",password="xxxxtest",host="smtp.126.com")
concent="hello!"
yag.send(to="test@qq.com",subject="主题test",contents=concent)

上述邮箱密码已经换成无法使用的,请替换后运行,SMTP那一行host不能少,否则报错。

测试图片

参考资料 1. python自动发邮件库yagmail

urllib、urllib2库简单使用(python2.7)

1. 用urllib2库发送HTTP请求:

#-*- coding:utf-8 -*-
import urllib2
import urllib

URL="http://www.baidu.com"
r=urllib2.urlopen(URL)
print r.read()

2. 带有header头的HTTP请求

import urllib2
import urllib

URL="http://www.baidu.com"
header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:59.0) Gecko/20100101 Firefox/59.0'}
req=urllib2.Request(url=URL,headers=header)
r=urllib2.urlopen(req)
print r.read()

3. 含有数据的HTTP请求(post)

#-*- encoding:utf-8 -*-
import urllib2
import urllib

URL="https://www.example.com/"
D={'wd':'苏州工业园区'}
Data=urllib.urlencode(D)
req=urllib2.Request(url=URL,data=Data)
r=urllib.urlopen(req)
print r.read()

注:百度仅支持get提交搜索,因此post直接复制执行会报错,上述例子适用于提交表单。

4. 含有数据的HTTP请求(get)

#-*- encoding:utf-8 -*-
import urllib2
import urllib

URL="http://www.baidu.com/s"
D={'wd':'苏州工业园区'}
Data=urllib.urlencode(D)
print Data
r=urllib2.urlopen(url=URL+"?"+Data)
print r.read()
print r.url

注:因为代码含有中文,因此,必须加入#-*- encoding:utf-8 -*-注释

5. 使用代理

import urllib2
httpproxy_hander=urllib2.ProxyHandler({'http':'60.208.217.25:8118'})
opener=urllib2.build_opener(httpproxy_hander)
header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:59.0) Gecko/20100101 Firefox/59.0'}
req=urllib2.Request("http://ip.chinaz.com/getip.aspx",headers=header)
r=opener.open(req)
print r.read()
print r.url

注:因为网站限制,所以,程序访问时必须添加headers内容。

60.208.217.25:8118为有效的代理ip #验证有效时间:2018/2/5 0:16:07