介绍requests+threading多线程爬虫,提取采用xpath 和正则两种,介绍线程锁

news/2024/7/19 11:31:23 标签: 爬虫, json, python

爬虫专业的都喜欢scrapy框架,但scrapy上手需要时间,对初学者不太适合。

本文介绍使用requets爬虫,为了利于演示学习,使用了xpath解析html和完全使用正则来提取两种方法,仅供参考。

代码是爬取http://esf.sz.fang.com/,房天下网站的深圳二手房信息

import requests,json,random
import re,threading
from lxml import etree

lock=threading.Lock()

user_agent_list = [ \
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1" ,\
        "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11", \
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6", \
        "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6", \
        "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1", \
        "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5", \
        "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5", \
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", \
        "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", \
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", \
        "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3", \
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3", \
        "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", \
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", \
        "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", \
        "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3", \
        "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24", \
        "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24"
    ]
count=0

def fang_com(page_url):    ##列表页

    header={}

    header['User-Agent']=random.choice(user_agent_list)
    header.update({
        "Host":"esf.sz.fang.com",
        #"Cookie":"global_cookie=fb1g6d0w64d2cmu86sv4g9n3va0j137sk48; vh_newhouse=3_1491312022_2816%5B%3A%7C%40%7C%3A%5D833300ee3177d88529c7aa418942ece9; newhouse_user_guid=2F163DE7-8201-7FA9-2FB6-E507FE6F03B1; SoufunSessionID_Esf=3_1495389730_232; sf_source=; s=; showAdsh=1; hlist_xfadhq_SZ=0%7c2017%2f5%2f25+1%3a21%3a47%7c; city=sz; __utmt_t0=1; __utmt_t1=1; __utmt_t2=1; logGuid=a768dd46-b85b-47f4-a7a0-0a6596cab4cd; __utma=147393320.1111837171.1491290389.1495646208.1495650134.9; __utmb=147393320.12.10.1495650134; __utmc=147393320; __utmz=147393320.1495650134.9.4.utmcsr=esf.sz.fang.com|utmccn=(referral)|utmcmd=referral|utmcct=/; unique_cookie=U_cqyov4ut5vv1al8e2858qhzgt17j2z06mph*14"
        })
    while(1):                     ###这个主要是,fang.com会随机返回几个10054或者10053,如果连页面都没读取到,提取就是后话了,这网站没有封杀,即使使用单ip只会很少时候随机来几个10054 ,('Connection aborted.', error(10054, ''))
        text=''
        try:
            text=requests.get(page_url,headers=header,timeout=10).text
            #print text
        except Exception as e:
            print e
        if text!='':
            break

    se = etree.HTML(text)                                 ###########为了利于大家学习,这段演示xpath提取信息
    all_dl=se.xpath('//dl[@class="list rel"]')
    print len(all_dl)
    for dl in all_dl:
        title=dl.xpath('.//dd[@class="info rel floatr"]/p[@class="title"]/a/text()')[0]

        url=dl.xpath('.//dd[@class="info rel floatr"]/p[@class="title"]/a/@href')[0]
        url='http://esf.sz.fang.com'+url

        info_list=dl.xpath('.//dd[@class="info rel floatr"]/p[@class="mt12"]/text()')
        #print json.dumps(info,ensure_ascii=False)    #py2显示汉字,py3可以直接print mt12
        info=''
        for l in info_list:
            l2= re.findall('\S*',l)[0]          ###消除空白和换行
            #print m_str
            info+=l2+'|'

        time.sleep(1)
        total_price,price_squere,huxin,cankao_shoufu,shiyong_mianji,jianzhu_mianji,years,discription=get_detail(url)


        lock.acquire()                  ###这里叫锁,一是保证count计数准确,而是不会导致多个线程乱print,导致看不清楚。加锁的目的是别的线程不能运行这段代码了。但我之前看到有的人乱加锁,把消耗时间很长的代码加锁,那样导致多线程就基本个废物
        global count
        count+=1
        print time.strftime('%H:%M:%S',time.localtime(time.time())),'    ',count
        print '列表页:'
        print ' title: %s\n url: %s\n info: %s\n'%(title,url,info)

        print '详情页:'
        print ' total_price: %s\n price_squere: %s\n huxin: %s\n cankao_shoufu: %s\n shiyong_mianji: %s\n jianzhu_mianji: %s\n years: %s \n'%(total_price,price_squere,huxin,cankao_shoufu,shiyong_mianji,jianzhu_mianji,years)
        print '**************************************************************'
        lock.release()



def get_detail(url):    ###详情页

    header={'User-Agent':random.choice(user_agent_list)}
    header.update({"Host":"esf.sz.fang.com"})

    while(1):
        content=''
        try:
            content=requests.get(url,headers=header,timeout=10).content
        except Exception as  e:
            print e
            pass
        if content!='':
            break

    content=content.decode('gbk').encode('utf8')  ##查看网页源代码可看到是gbk编码,直接print的话,如果你在pycharm设置控制台是utf8编码,那么控制台的中文则会乱码,cmd是gbk的恰好可以显示。如果你在pycharm设置控制台是utf8编码,需要这样做
    #print content

    inforTxt=getlist0(re.findall('(<div class="inforTxt">[\s\S]*?)<ul class="tool">',content))       ###########为了利于大家学习,这段演示正则表达式提取信息,某些信息可能在有的房子界面没有,要做好判断
    #print inforTxt

    total_price=getlist0(re.findall('</span>价:<span class="red20b">(.*?)</span>',inforTxt))

    price_squere=getlist0(re.findall('class="black">万</span>\((\d+?)元[\s\S]*?<a id="agantesfxq_B02_03"',inforTxt))
    huxin=getlist0(re.findall('<dd class="gray6"><span class="gray6">户<span class="padl27"></span>型:</span>(.*?)</dd>',inforTxt))
    cankao_shoufu=getlist0(re.findall('参考首付:</span><span class="black floatl">(.*?万)</span> </dd>',inforTxt))
    shiyong_mianji=getlist0(re.findall('>使用面积:<span class="black ">(.*?)</span></dd>',inforTxt))
    shiyong_mianji=getlist0(re.findall('\d+',shiyong_mianji))
    jianzhu_mianji=getlist0(re.findall('建筑面积:<span class="black ">(.*?)</span></dd>',inforTxt))
    jianzhu_mianji=getlist0(re.findall('\d+',jianzhu_mianji))
    years=getlist0(re.findall('<span class="gray6">年<span class="padl27"></span>代:</span>(.*?)</dd>',inforTxt))

    discription=getlist0(re.findall('style="-moz-user-select: none;">([\s\S]*?)<div class="leftBox"',content))
    #print discription
    #print total_price,price_squere,huxin,cankao_shoufu,shiyong_mianji,jianzhu_mianji,years

    return total_price,price_squere,huxin,cankao_shoufu,shiyong_mianji,jianzhu_mianji,years,discription




#get_detail('http://esf.sz.fang.com/chushou/3_193928457.htm')
def getlist0(list):
    if list:
        return list[0]
    else:
        return ''

if __name__=='__main__':
    '''                                       ##这个是单线程,单线程爬很慢,3000个房子信息,一个5秒,那也得15000秒了,很耽误时间
    for i in range(1,101):
        page_url='http://esf.sz.fang.com/house/i3%s'%i
        fang_com(page_url)
    '''
    threads=[]                                            ###这个是演示多线程爬取
    for i in range(1,101):                                             #开了100线程,这样开100线程去爬100页面的详情页面,因为fang.com只能看100页
        t=threading.Thread(target=fang_com,args=('http://esf.sz.fang.com/house/i3%s'%i,))           ###这样做没问题,但如果你是爬取1000页面,也这样做就不合适了,python开多了线程会导致线程创建失败,100线程已经很快了,网速是瓶颈了这时候,我开100线程时候网速是800KB左右的网速,我宽带才4M,运营商还算比较良心了,4M宽带400k

        threads.append(t)

        t.start()

    for t in threads:
        t.join()

    print 'over'

 

多线程不到3分钟就爬取完3000个房源的详细信息了,发下多线程的运行结果:

 


http://www.niftyadmin.cn/n/843470.html

相关文章

js实现将时分秒转化成毫秒,将秒转化成时分秒

由于功能比较简单&#xff0c;那就直接上代码吧&#xff01;// 时间转为毫秒 timeToSec(time) {var hour time.split(:)[0]var min time.split(:)[1]var sec time.split(:)[2]var s Number(hour * 3600) Number(min * 60) Number(sec)return s * 1000 }// 将秒转化成时分…

转 Python常见数据结构整理

http://www.cnblogs.com/jeffwongishandsome/archive/2012/08/05/2623660.html Python常见数据结构整理 Python中常见的数据结构可以统称为容器&#xff08;container&#xff09;。序列&#xff08;如列表和元组&#xff09;、映射&#xff08;如字典&#xff09;以及集合&…

JAVA基础 写一个方法,输入一个文件名和一个字符串,统计这个字符串在这个文件中出现的次数

读取一行循环 行里搜索word,记录总次数 方法&#xff1a;每遇到一个word就并且裁剪包括这个word的前一部分。 import java.io.BufferedReader; import java.io.FileReader;public final class MyUtil {// 工具类中的方法都是静态方式访问的因此将构造器私有不允许创建对象(绝对…

vue实现数字滚动效果并匀速滚动

最近有一个需求就是&#xff1a;要求显示两个数之间的匀速滚动效果。一开始我是直接从网上找了一个插件&#xff0c;但是这个插件并不是匀速滚动的&#xff0c;这就导致当页面刷新后显示的数字不是刷新之前的&#xff0c;这样肯定不行啊&#xff0c;那么我们就思考将24小时分成…

jquery中常见问题及解决办法小结

1 在开发开放聊天室的过程中&#xff0c;遇到使用ajax提交表单插入数据库时会插入两条数据的情况 解决办法&#xff0c;在ajax函数返回后&#xff0c;return false. $("#btn").click(function(){ $.ajax({do something }); return false;}) 2 去除选中元素的某一个属…

使用vue+elementUI页面实现前端做分页

当数据不是很多的时候&#xff0c;后端会要求前端来实现分页功能。那么下面讲下怎么使用element来实现前端做分页功能。由于功能比较简单&#xff0c;那么就直接上代码&#xff1a; // 计算当前页面的数据 tableList() {// this.displayData是当前页面要显示的数据this.displa…

LeetCode刷题笔记 字节每日打卡 重复的子字符串

给定一个非空的字符串 s &#xff0c;检查是否可以通过由它的一个子串重复多次构成。 示例 1: 输入: s "abab" 输出: true 解释: 可由子串 "ab" 重复两次构成。 参考&#xff1a; 力扣 ​​​​​​ 力扣 语言自带方法 从第一个位置开始搜索&#xf…

Django基础(三)session会话、认证系统、内容分页、中间件

一、Cookies & SessionCookies:由于http协议是无状态的在一次请求和下一次请求之间没有任何状态保持我们无法根据请求的任何方面来识别来自同一人的连续请求。然而浏览器的开发者在很早的时候就已经意识到 HTTP 的无状态会对Web开发者带来很大的问题于是(cookies)应运而生。…