1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > python爬虫——用selenium和phantomjs对新浪微博PC端进行爬取(二)

python爬虫——用selenium和phantomjs对新浪微博PC端进行爬取(二)

时间:2020-11-22 09:47:05

相关推荐

python爬虫——用selenium和phantomjs对新浪微博PC端进行爬取(二)

。,。上一篇文章里我选择爬取简单的微博移动端,由于移动端构造简单,一般都优先爬取移动端,且因为是静态页面,我们可以直接使用xpath或者正则表达式搞定,但pc端结构就复杂得多,不能使用前面的方法。这篇文章我尝试使用selenium加phantomjs的组合,模拟人的操作来对指定微博的PC端进行爬取。

这次我们选择的种子网页依旧是工商秘密微博,但爬取信息范围更广,我们需要爬虫不止拿到种子网页的信息,它的粉丝信息我们也希望获取到,包括粉丝的ID,地址,微博数量,关注人数,粉丝人数等,然后又以粉丝为节点循环下去,爬取到以工商秘密为网络中心点的大量信息。

文章是我在学习网络爬虫课程期间为了加深知识的理解写的,多有不足,希望各位不吝指教。

首先,这次的爬取分为两个部分,分别是对节点微博的信息爬取,对其粉丝的信息爬取。所以,我们构建两个爬虫程序,一个爬取的信息,一个爬取其粉丝的信息。获取到粉丝主页的url后又以其作为节点循环,两个爬虫之间用url填充的双端序列联系,粉丝爬虫将获取到的url加入序列,节点爬虫将其取出爬取。

由于爬取链接极多,且为同一微博的粉丝,极可能抓取到重复的链接,这里我们使用布隆波过滤器进行网页链接的排重。

cur_queue=deque() download_bf=BloomFilter(1024*1024*16,0.01)

首先构造一个存储下载链接的双端队列,然后设定好BloomFilter函数的碰撞概率为0.01

cur_driver=webdriver.PhantomJS(service_args=['--ssl-protocol=any', '--load-images=false']) follows_driver=webdriver.PhantomJS(service_args=['--ssl-protocol=any', '--load-images=false'])

cur_driver.set_window_size(1280,2400) #设窗口的大小follows_driver.set_window_size(1280,2400)

分别创建好cur_driver和follows_driver两个phantomjs浏览器对象,对他们的属性参数进行设置,由于我们使用这种爬取方式本身速度就很慢,所以把‘--load-images’属性设置为false,不下载图片,且phantomjs本身就是一个没有UI界面的浏览器,selenium则是浏览器自动化测试软件,我将selenium发出的命令理解为人操作phantomjs浏览器所发出的动作,而微博主页信息设置为需要滚屏下拉才能查看,且网速不好会出现重新加载的提示,针对这种情况,我们专门设置一个滚屏的函数。

def scroll_to_bottom():# 最多尝试 20 次滚屏print ("开始滚屏")for i in range(0,50):follows_driver.execute_script('window.scrollTo(0, document.body.scrollHeight)')html = follows_driver.page_sourcetr = etree.HTML(html)next_page_url = tr.xpath('//a[contains(@class,"page next")]')if len(next_page_url) > 0:return next_page_url[0].get('href')if len(re.findall('点击重新载入', html)) > 0:print ('滚屏失败了,请刷新')follows_driver.find_element_by_link_text('点击重新载入').click()time.sleep(1)

在这个函数里,我们使用了selenium的script代码来执行向下滚屏的操作,用if语句来判断是否出现了重新加载的提示,如果有就点击它重新加载。

这里可以提一下,其实selenium自身有很多html和script的代码,可以用来查询修改网页标签属性等,不过速度实在太慢,但其优势在于可以对网页进行交互式操作,类似点击等,这是xpath,re等纯提取信息的库做不到的。

def go_next_page(driver):try:next_page = driver.find_element_by_xpath('//a[contains(@class, "page next")]').get_attribute('href')print ('进入下一页' + next_page)driver.get(next_page)time.sleep(3)return Trueexcept Exception:print ("找不到下一页了")return False

处理完向下滚屏后,网页的翻页行为也是需要我们自己定义的,我们依旧将翻页行为独立出来,定义为一个单独的函数,这里selenium查找标签使用的就是xpath的方法,定位到了下一页标签,获取到href然后传输给浏览器对象完成翻页行为。

def enqueurl(url) :try:md5v = hashlib.md5(str(url).encode('utf-8')).hexdigest()if md5v not in download_bf:print (url + '进入爬取队列')cur_queue.append(url)download_bf.add(md5v)except ValueError:pass

对于网页链接重复的问题,我们使用布隆波函数来处理,bloomfilter的参数我们在上面已经定义了,对于新抓取到的url我们需要判断它是否被爬取过,在进入双端序列前对它进行检查,这里要注意,我们必须先将url编码,否则无法将其哈希化,然后转化成16进制。使用if语句判断url是否已经存在于down_bf下载过的列表,如果是就直接pass,如果不是就将其加入双端队列和down_bf队列。

def login(username,password):print ("开始登录")cur_driver.get(url_home)print(len(cur_driver.page_source))follows_driver.get(url_home)time.sleep(4)print ("开始输入密码")cur_driver.find_element_by_id('loginname').send_keys(username)cur_driver.find_element_by_name('password').send_keys(password) cur_driver.find_element_by_xpath('//div[contains(@class,"login_btn")][1]/a').click()print (len(cur_driver.page_source))follows_driver.find_element_by_id('loginname').send_keys(username)follows_driver.find_element_by_name('password').send_keys(password) follows_driver.find_element_by_xpath('//div[contains(@class,"login_btn")][1]/a').click()

开始处理登陆的问题,这里就体现出了selenium模拟人操作的简便性,如果是直接用post的方式登陆,那就涉及到加密等相当麻烦的问题,而使用selenium就特别简单,我们只需要在网页中找到输入账号密码的标签,然后用selenium自带的send_keys对其进行复制就可以了,输完账号密码再找到登陆的标签,使用click()方法点击就可以成功登陆微博了。

def crawl():while True: url=cur_queue.popleft()print(url)fech_user(url)

def fech_user(url_link): print("开始爬取用户"+ url_link) follows_driver.get(url_link)time.sleep(5)account_name = get_element_by_xpath(follows_driver,'//h1')[0].textfollow_link = get_element_by_xpath(follows_driver,'//a[@clss="t_link S_text"]')[1].get('href')print('用户名' + account_name)print('粉丝列表' + follow_link)cur_driver.get(follow_link) feeds=[]users=[]extract_feed(feeds)extract_user(users)

登录之后开始我们的爬虫程序,首先构造一个while循环,所要爬取的链接用.popleft()方法获取左边的url,然后传入进我们的处理函数fech_user,fech_user函数的任务是抓取到我们目标微博的ID和它粉丝列表的链接,此处将对粉丝信息的爬取和对节点用户的信息抓取分开处理,构造feeds和users两个空序列来存储我们所抓取到的信息,而extract_feed和extract_user两个函数则分别来提取信息。

def extract_feed(feeds): print("开始提取微博发布信息")for i in range(0,20):scroll_to_bottom() for element in follows_driver.find_elements_by_class_name('WB_detail')tried = 0while tried<3:try:feed={}feed['time'] = element.find_element_by_xpath('.//div[@class="WB_form S_txt2"]').textfeed['content'] = element.find_element_by_class_name('WB_text').textfeeds.append(feed)print(feed['time'])print(feed['content'])breakexcept Exception:tried += 1time.sleep(1)if go_next_page(follows_driver) is False:return feeds

extract_feed函数用来提取我们指定的节点微博的信息,由于页面微博的信息设计到滚屏翻页的操作,这里我们使用一个for循环,最多滚屏20次,接下来对网页的信息标签进行分析,我们想要的数据时节点微博所发的微博内容和发该微博的时间,找到对应的标签,这里分别使用了xpath和class name来对标签进行定位,feed定义为字典类型,将节点微博所发的微博内容和时间作为键值对存储,然后放到我们先前定义的序列feeds中,由于网络的原因,加上这种方法本来加载也慢,我们需要经常使用time。sleep()方法来等待它。

def extract_user(users): print("开始提取粉丝信息")for i in range(0,20):

scroll_to_bottom()

for user_element in cur_driver.find_elements_by_xpath('//li[contains(@class,"follow_item")]'): tried = 0 while tried<8: try: user={} user['name'] = user_element.find_elements_by_xpath('//a[contains(@class,"S_txt1")]')[0].textuser['follows'] = user_element.find_elements_by_xpath('//em[@class="count"]/a')[0].text user['fans'] = user_element.find_elements_by_xpath('//em[@class="count"]/a')[1].text user['blogs'] = user_element.find_elements_by_xpath('//em[@class="count"]/a')[2].textuser['adress'] = user_element.find_elements_by_xpath('div[@class="info_add"]/span')[0].text users.append(user) print("----------------------------------------------------") print(user['name']+"关注"+user['follows']+ "粉丝"+user['fans']+"微博数"+user['blogs']+"地址"+user['adress'])except Exception: time.sleep(1) tried+=1 if go_next_page(user_crawler) is False: return user return user

开始构造extract_users函数,我们定义他来实现提取粉丝信息的功能,在前面我们获取到了节点用户粉丝链接后,将该链接传入到了extract——users函数,相当于我们的浏览器已经进入到了节点用户的粉丝网页中,这里同样用for循环进行滚屏20次的操作,点开粉丝的列表,我们想要获取到的信息有粉丝的年龄,地址,关注人数,他的粉丝数量,他所发的微博数,接下来我们使用开发者工具对网页中这些信息的元素进行分析,我们可以发现所有我们需要的信息都是存在li标签下,新浪应该是以li标签来对每个粉丝的信息进行存储,找到了对应的大标签,我们直接对其进行遍历,然后使用xpath的方法将其取出保存到我们定义的序列中。至此,重要的几个功能函数我们都已经定义完成,最后,我们开始构造程序的主函数。

def main():enqueurl(start_url)login(username,password)crawl()cur_driver.close()follows_driver.close()main()

好了,主函数先调用enque函数,将种子网页加入到双端序列中,然后输入用户名和密码登陆新浪微博,在登陆之后运行爬虫程序,让他们自己循环抓取数据,最后记得使用.close()方法关闭我们创建的两个浏览器对象,由于phantomjs是独立于python的,所以如果我们不关闭它,它会一直在后台运行。

下面给出全部代码

#-*-coding:utf-8-*-import hashlibimport threadingfrom collections import dequefrom selenium import webdriverimport refrom lxml import etreeimport timefrom pybloom import BloomFilterfrom mon.desired_capabilities import DesiredCapabilitiesuser_agent= 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/60.0.3112.78 Chrome/60.0.3112.78 Safari/537.36' domain=""url_home="http://" + domain start_url="/234687487?"cur_driver=webdriver.PhantomJS(service_args=['--ssl-protocol=any', '--load-images=false']) follows_driver=webdriver.PhantomJS(service_args=['--ssl-protocol=any', '--load-images=false'])cur_driver.set_window_size(1280,2400)follows_driver.set_window_size(1280,2400)download_bf=BloomFilter(1024*1024*16,0.01) cur_queue=deque() def get_element_by_xpath(cur_driver, path): #我们将xpath提取元素的方法单独列出一个函数,避免每次都写一长串代码去实现xpath功能tried = 0while tried < 6:html = cur_driver.page_sourcetr = etree.HTML(html)elements = tr.xpath(path)if len(elements) == 0:time.sleep(1)continuereturn elementsdef extract_user(users): print("开始提取粉丝信息")for i in range(0,20):for user_element in cur_driver.find_elements_by_xpath('//li[contains(@class,"follow_item")]'):tried = 0while tried<8:try:user={}user['name'] = user_element.find_elements_by_xpath('//a[contains(@class,"S_txt1")]')[0].textuser['follows'] = user_element.find_elements_by_xpath('//em[@class="count"]/a')[0].textuser['fans'] = user_element.find_elements_by_xpath('//em[@class="count"]/a')[1].textuser['blogs'] = user_element.find_elements_by_xpath('//em[@class="count"]/a')[2].textuser['adress'] = user_element.find_elements_by_xpath('div[@class="info_add"]/span')[0].textprint("----------------------------------------------------")print(user['name']+"关注"+user['follows']+ "粉丝"+user['fans']+"微博数"+user['blogs']+"地址"+user['adress'])except Exception:time.sleep(1)tried+=1if go_next_page(user_crawler) is False:return userreturn userdef extract_feed(feeds):print("开始提取微博发布信息")for i in range(0,20):scroll_to_bottom() #向下滚屏for element in follows_driver.find_elements_by_class_name('WB_detail'):tried = 0while tried<3:try:feed={}feed['time'] = element.find_element_by_xpath('.//div[@class="WB_form S_txt2"]').textfeed['content'] = element.find_element_by_class_name('WB_text').textfeeds.append(feed)print(feed['time'])print(feed['content'])breakexcept Exception:tried += 1time.sleep(1)if go_next_page(follows_driver) is False:return feedsdef scroll_to_bottom():# 最多尝试 20 次滚屏print ("开始滚屏")for i in range(0,50):follows_driver.execute_script('window.scrollTo(0, document.body.scrollHeight)')html = follows_driver.page_sourcetr = etree.HTML(html)next_page_url = tr.xpath('//a[contains(@class,"page next")]')if len(next_page_url) > 0:return next_page_url[0].get('href')if len(re.findall('点击重新载入', html)) > 0:print ('滚屏失败了,请刷新')follows_driver.find_element_by_link_text('点击重新载入').click()time.sleep(1)def go_next_page(driver):try:next_page = driver.find_element_by_xpath('//a[contains(@class, "page next")]').get_attribute('href')print ('进入下一页' + next_page)driver.get(next_page)time.sleep(3)return Trueexcept Exception:print ("找不到下一页了")return Falsedef enqueurl(url) :try:md5v = hashlib.md5(str(url).encode('utf-8')).hexdigest()if md5v not in download_bf:print (url + '进入爬取队列')cur_queue.append(url)download_bf.add(md5v)except ValueError:passdef crawl():while True: url=cur_queue.popleft()print(url)fech_user(url)def fech_user(url_link): print("开始爬取用户"+ url_link) follows_driver.get(url_link)time.sleep(5)account_name = get_element_by_xpath(follows_driver,'//h1')[0].textfollow_link = get_element_by_xpath(follows_driver,'//a[@clss="t_link S_text"]')[1].get('href')print('用户名' + account_name)print('粉丝列表' + follow_link)cur_driver.get(follow_link) feeds=[]users=[]extract_feed(feeds)extract_user(users)def login(username,password):print ("开始登录")cur_driver.get(url_home)print(len(cur_driver.page_source))follows_driver.get(url_home)time.sleep(4)print ("开始输入密码")cur_driver.find_element_by_id('loginname').send_keys(username)cur_driver.find_element_by_name('password').send_keys(password) cur_driver.find_element_by_xpath('//div[contains(@class,"login_btn")][1]/a').click()print (len(cur_driver.page_source))follows_driver.find_element_by_id('loginname').send_keys(username)follows_driver.find_element_by_name('password').send_keys(password) follows_driver.find_element_by_xpath('//div[contains(@class,"login_btn")][1]/a').click()def main():enqueurl(start_url)login(username,password)crawl()cur_driver.close()follows_driver.close()main()

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