1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > python实现一个很简单的多线程爬虫

python实现一个很简单的多线程爬虫

时间:2020-06-17 13:17:10

相关推荐

python实现一个很简单的多线程爬虫

需求

无聊用python写一个爬虫爬取某个视频网站的内容,获取名称地址时长和更新时间保存到数据库头,以前没用过也没学多线程所以接直接顺着来,按照python的简洁性几行代码就搞定,一刷下来没问题,保存到数据库好好的,可以六百多页视频,差不多要半个小时才搞定,多可以打一把王者了,看控制台发现就是卡在每页访问比较慢要等请求响应的过程。这就是多线程编程的典型问题等待IO过程比较慢先做其他事。

多线程网络请求问题

因为是每页抓取的速度比较慢导致整体慢的,所以用多线程进行请求,在等待一个线程响应的过程中另外再多次发起请求,每个线程对一个页面进行请求和处理保存。所以涉及到防止重复获取相同页面的问题,所以需要一个多线程共享的页面参数。共享变量的访问和修改进行加锁保证每次只有一个线程访问修改

#加锁lock.acquire()global pageif page >= 666:lock.release()breakurl = '/html/category/video/video2/page_'+str(page)+'.html'page = page+1lock.release()

使用threading.Thread()方式进行函数式开启多线程,开启线程的数量根据个人电脑情况而定(网速和C电脑性能),并且使用线程的join()函数进行堵塞等待所有线程结束主线程才结束方便计时

threads = []for i in range(0,60):t = threading.Thread(target=get_message, args=( ))threads.append(t)t.start()for t in threads:t.join()

多线程访问数据库的问题

本次实践用的是MySQL数据库,首先通过多线程共享一个连接时报错,网上找pymysqll的execute有独占锁机制啥的,反正就是数据库连接不是线程安全的不可以多线程公用一个数据库连接,如果多线程访问会发生冲突问题,可以每个线程单独用一个连接,或者在访问数据库时进行加锁,显然每个线程独用一个连接更加高效。考虑到数据库连接的创建和销毁花销比较大,所以追求更加高效的方法——连接池。基于面向模块编程的python,我知道我想到的别人应该已经写出来了,网上一找通过别人实践证明DBUtils最好用效率最高。直接搜索用法就可以了。

#安装DBUtils包#pip install DBUtilsfrom DBUtils.PooledDB import PooledDBpool = PooledDB(pymysql,4,host='localhost',user='root',passwd='123456',db='key_count') #4为连接池里的最少连接数def test(project_name):#取一个连接global pooldb = pool.connection()cursor = db.cursor()#保存到数据库try :cursor.execute(sql)mit()except :db.rollback()cursor.close()#放回连接池db.close()

其中第二个参数是连接池里最少有4个连接的意思。其他用默认就好了

总结

多线程爬虫是实践多线程学习的经典方式,能够很快就体验到多线程的好处。本次实践本来需要一把王者的时间才能爬取完成,开启多线程后都不到一首歌的时间。

总代码

import urllib.requestimport datetimeimport timeimport reimport pymysqlimport threadingfrom bs4 import BeautifulSoupfrom http import cookiejarfrom urllib.error import URLErrorfrom DBUtils.PooledDB import PooledDBdef get_message():while True:#加锁lock.acquire()global pageif page >= 666:lock.release()breakurl = '/html/category/video/video2/page_'+str(page)+'.html'page = page+1lock.release()#创建cookie会话cookie = cookiejar.CookieJar()headler = urllib.request.HTTPCookieProcessor(cookie)opener = urllib.request.build_opener(headler)#发起请求并获取相关信息response = opener.open(urllib.request.Request(url))h = BeautifulSoup(response, 'html.parser')for i in range(len(h.findAll('h3'))):name = h.findAll('h3')[i].a.getText().replace(' ','')print(name,type(name),len(h.findAll('h3')))address = h.findAll('h3')[i].a['href']print(address,type(address))update_time = re.sub(r'号|日','',h.findAll('span',{'class':'s_j'})[i].getText().replace('年','-').replace('月','-'))print(update_time,type(update_time))length = h.findAll('span',{'class':'z_s'})[i].getText().split(':')[1].split('\'')[0]print(length,type(length))sql = f"insert into resources2 (\name,address,length,update_time\)values(\{name!r},{address!r},{length},{update_time!r}\)"print(sql)#连接数据库global pooldb = pool.connection()cursor = db.cursor()#保存到数据库try :cursor.execute(sql)mit()except :db.rollback()cursor.close()#释放回连接池db.close()if __name__ == '__main__':page = 1lock = threading.Lock()start = time.time()threads = []pool = PooledDB(pymysql,4,host='localhost',user='root',passwd='123456',db='key_count') for i in range(0,60):t = threading.Thread(target=get_message, args=( ))threads.append(t)t.start()for t in threads:t.join()print('运行了:',int(time.time()-start),'s')

改进

网络请求获取数据部分

可以将网络请求和数据处理进行分开线程处理,一部分线程进行网络请求,一部分进行数据处理(生产者消费者模式),用Queue等线程安全的结构进行线程间通讯。本次由于处理数据并不复杂所以放在一个线程同时处理问题不大。数据保存部分

可以先将数据封装成SQL的文件再导入数据库就不存在访问数据库线程安全问题

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。