요즘에는 Node.js나 Go, 심지어 PHP까지 웹 크롤러를 작성하는 방법이 많습니다. 제가 Python을 선택한 이유는 튜토리얼도 많고, 배우기만 하면 체계적으로 배울 수 있기 때문입니다. HTML 선택기를 사용하여 크롤링하는 방법을 알고 있습니다. 페이지만으로는 충분하지 않습니다. 또한 크롤링 과정에서 발생할 수 있는 몇 가지 일반적인 함정과 브라우저 헤더 수정에 대한 팁과 같은 몇 가지 주의 사항도 알고 싶습니다.
코드 주석이 아주 자세하게 나와있습니다. 사실 소스코드를 직접 읽어보기만 하면 됩니다.
이 크롤러의 목적은 매우 간단합니다. 부동산 웹사이트의 이름 + 가격 + 사진 다운로드 1장을 크롤링하여 나중에 주택 가격 추세를 분석하는 것입니다. 상대방 서버의 부담이 너무 커져서 3페이지만 크롤링하기로 결정했습니다.
주의해야 할 몇 가지 지식 사항에 대해 이야기하겠습니다.
#보내는 헤더 수정을 기억하세요
기본적으로 Python 정보가 포함된 헤더가 전송된다고 들었습니다. , 정말 이상한 일입니다. 다른 웹사이트에서는 크롤러 로봇으로 감지되기 쉽기 때문에 IP가 차단될 수 있으므로 크롤러 프로그램을 좀 더 인간처럼 만드는 것이 가장 좋지만 이 코드는 일반 기능만 수행할 수 있습니다. 크롤러를 막고 싶은 웹사이트가 있다면 역시 거짓말을 하는 것입니다. 그러나 위의 코드는
headers = {"User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit 537.36 (KHTML, like Gecko) Chrome", "Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"},
#html 선택기, beautifulsoup 대신 pyquery를 사용합니다
많은 책에서 beautifulsoup를 추천합니다. , 하지만 jquery에 익숙한 사람으로서 beautifulsoup의 구문은 정말 말이 좀 많고, first-child와 같은 고급 및 복잡한 CSS 선택기 모드를 지원하지 않거나 지원할 수도 있는 것 같습니다. , 그러나 찾을 수 없었고 설명서를 주의 깊게 읽지도 않았습니다.
그러다가 온라인에서 정보를 찾아보니 많은 분들이 pyquery 라이브러리를 추천해 주셨고, 직접 써보니 정말 편하다고 해서 과감하게 채택하게 되었습니다.
#크롤러 아이디어
아이디어는 실제로 매우 간단합니다.
1. 특정 부동산의 목록 페이지를 찾아 두 번째 및 세 번째 페이지의 URL 구조를 분석합니다.
2. 각 목록 페이지의 모든 목록 항목 정보의 URL은 Python의 set() 컬렉션에 저장됩니다. set을 사용하는 이유는 중복된 URL 정보를 제거하기 위한 것입니다.
3. 집의 URL을 얻어 세부정보 페이지에 진입한 후, 사진, 텍스트 등 귀중한 현장 정보를 크롤링합니다.
4. 현재는 데이터만 인쇄하고 로컬 json 또는 CSV 형식으로 저장하지 않습니다.
전체 코드는 다음과 같습니다.
#获取页面对象 from urllib.request import urlopen from urllib.request import urlretrieve from pyquery import PyQuery as pq #修改请求头模块,模拟真人访问 import requests import time #引入系统对象 import os #你自己的配置文件,请将config-sample.py重命名为config.py,然后填写对应的值即可 import config #定义链接集合,以免链接重复 pages = set() session = requests.Session() baseUrl = 'http://pic1.ajkimg.com' downLoadDir = 'images' #获取所有列表页连接 def getAllPages(): pageList = [] i = 1 while(i < 2): newLink = 'http://sh.fang.anjuke.com/loupan/all/p' + str(i) + '/' pageList.append(newLink) i = i + 1 return pageList def getAbsoluteURL(baseUrl, source): if source.startswith("http://www."): url = "http://"+source[11:] elif source.startswith("http://"): url = source elif source.startswith("www."): url = "http://"+source[4:] else: url = baseUrl+"/"+source if baseUrl not in url: return None return url #这个函数内部的路径按照自己的真实情况来写,方便之后的数据导入 def getDownloadPath(baseUrl, absoluteUrl, downloadDirectory): path = absoluteUrl.replace("www.", "") path = path.replace(baseUrl, "") path = downloadDirectory+path directory = os.path.dirname(path) if not os.path.exists(directory): os.makedirs(directory) return path #获取当前页面的所有连接 def getItemLinks(url): global pages; #先判断是否能获取页面 try: req = session.get(url, headers = config.value['headers']) #这个判断只能判定是不是404或者500的错误,如果DNS没法解析,是无法判定的 except IOError as e: print('can not reach the page. ') print(e) else: h = pq(req.text) #获取第一页的所有房子模块 houseItems = h('.item-mod') #从模块中提取我们需要的信息,比如详情页的URL,价格,略缩图等 #我倾向只获取详情页的URL,然后在详情页中获取更多的信息 for houseItem in houseItems.items(): houseUrl = houseItem.find('.items-name').attr('href') #print(houseUrl) pages.add(houseUrl) #获取详情页的各种字段,这里可以让用户自己编辑 def getItemDetails(url): #先判断是否能获取页面 try: req = session.get(url, headers = config.value['headers']) #这个判断只能判定是不是404或者500的错误,如果DNS没法解析,是无法判定的 except IOError as e: print('can not reach the page. ') print(e) else: time.sleep(1) h = pq(req.text) #get title housePrice = h('h1').text() if h('h1') != None else 'none' #get price housePrice = h('.sp-price').text() if h('.sp-price') != None else 'none' #get image url houseImage = h('.con a:first-child img').attr('src') houseImageUrl = getAbsoluteURL(baseUrl, houseImage) if houseImageUrl != None: urlretrieve(houseImageUrl, getDownloadPath(baseUrl, houseImageUrl, downLoadDir)) # if bsObj.find('em',{'class','sp-price'}) == None: # housePrice = 'None' # else: # housePrice = bsObj.find('em',{'class','sp-price'}).text; # if bsObj.select('.con a:first-child .item img')== None: # houseThumbnail = 'None' # else: # houseThumbnail = bsObj.select('.con a:first-child .item img'); #start to run the code allPages = getAllPages() for i in allPages: getItemLinks(i) #此时pages 应该充满了很多url的内容 for i in pages: getItemDetails(i) #print(pages)