- 전체
- Python 일반
- Python 수학
- Python 그래픽
- Python 자료구조
- Python 인공지능
- Python 인터넷
- Python SAGE
- wxPython
- TkInter
- iPython
- wxPython
- pyQT
- Jython
- django
- flask
- blender python scripting
- python for minecraft
- Python 데이터 분석
- Python RPA
- cython
- PyCharm
- pySide
- kivy (python)
[python] mechanize 와 Beautifup soup를 이용한 웹 사이트 정보 수집
2012.10.11 21:31
[python] mechanize 와 Beautifup soup를 이용한 웹 사이트 정보 수집
Charming Python: Easy Web data collection with mechanize and Beautiful Soup
[출처] http://www.ibm.com/developerworks/linux/library/l-python-mechanize-beautiful-soup/index.html
Python tools make it easy to extract and organize Web site data
Summary: For collecting data from Web pages, the mechanize library automates scraping and interaction with Web sites. Mechanize lets you fill in forms and set and save cookies, and it offers miscellaneous other tools to make a Python script look like a genuine Web browser to an interactive Web site. A frequently used companion tool called Beautiful Soup helps a Python program makes sense of the messy "almost-HTML" that Web sites tend to contain.
Writing scripts to interact with Web sites is possible with the basic Python modules, but you don't want to if you don't have to. The modules urllib and urllib2 in Python 2.x, along with the unified urllib.* subpackages in Python 3.0, do a passable job of fetching resources at the ends of URLs. However, when you want to do any sort of moderately sophisticated interaction with the contents you find at a Web page, you really need the mechanize library (see Resources for a download link).
One of the big difficulties with automating Web scraping or other simulations of user interaction with Web sites is server use of cookies to track session progress. Obviously, cookies are part of HTTP headers and are inherently visible when urllib opens resources. Moreover, the standard modules Cookie (http.cookie in Python 3) and cookielib (http.cookiejar in Python 3) help in handling those headers at a higher level than raw text processing. Even so, doing this handling at this level is more cumbersome than necessary. The mechanize library takes this handling to a higher level of abstraction and lets your script—or your interactive Python shell—act very much like an actual Web browser.
Python's mechanize is inspired by Perl's WWW:Mechanize, which has a similar range of capabilities. Of course, as a long-time Pythonista, I find mechanize more robust, which seems to follow the general pattern of the two languages.
A close friend of mechanize is the equally excellent library Beautiful Soup (see Resources for a download link). This is a wonderful "sloppy parser" for the approximately valid HTML you often find in actual Web pages. You do not need to use Beautiful Soup with mechanize, nor vice versa, but more often than not you will want to use the two tools together as you interact with the "actually existing Web."
I have used mechanize in several programming projects. The most recent was a project to gather a list of names matching some criteria from a popular Web site. This site comes with some search facilities, but not with any official API for performing such searches. While readers might be able to guess more specifically what I was doing, I will change specifics of the code I present to avoid giving too much information on either the scraped site or my client. In general form, code very much like what I present will be common for similar tasks.
In the process of actually developing Web scraping/analysis code, I find it invaluable to be able to peek at, poke, and prod the content of Web pages in an interactive way in order to figure out what actually occurs on related Web pages. Usually, these are sets of pages within a site that are either dynamically generated from queries (but thereby having consistent patterns) or are pre-generated following fairly rigid templates.
One valuable way of doing this interactive experimentation is to use mechanize itself within a Python shell, particularly within an enhanced shell like IPython (see Resources for a link). Doing exploration this way, you can request various linked resources, submit forms, maintain or manipulate site cookies, and so on, prior to writing your final script that performs the interaction you want in production.
However, I find that much of my experimental interaction with Web sites is better performed within an actual modern Web browser. Seeing a page conveniently rendered gives a much quicker gestalt of what is going on with a given page or form. The problem is that rendering a page alone only gives half the story, maybe less than half. Having "page source" gets you slightly further. To really understand what is behind a given Web page or a sequence of interactions with a Web server, I find more is needed.
To get at these guts, I usually use the Firebug (see Resources for a link) or Web Developer plug-ins for Firefox (or the built-in optional Develop menu in recent Safari versions, but that's for a different audience). All of these tools let you do things like reveal form fields, show passwords, examine the DOM of a page, peek at or run Javascript, watch Ajax traffic, and more. Comparing the benefits and quirks of these tools is a whole other article, but do familiarize yourself with them if you do any Web-oriented programming.
Whatever specific tool you use to experiment with a Web site you intend to automate interaction with, you will probably spend many more hours figuring out what a site is actually doing than you will writing the amazingly compact mechanize code needed to perform your task.
For the purposes of the project I mentioned above, I split my hundred-line script into two functions:
- Retrieve all the results of interest to me
- Pull out the information that interests me from those retrieved pages
I organized the script this way as a development convenience; when I started the task, I knew I needed to figure out how to do each of those two things. I had a sense that the information I wanted was on a general collection of pages, but I had not yet examined the specific layout of those pages.
By first retrieving a batch of pages and just saving them to disk, I could come back to the task of pulling out the information I cared about from those saved files. Of course, if your task involves using that retrieved information to formulate new interactions within the same session, you will need to use a slightly different sequence of development steps.
So, first, let's look at my fetch() function:
Listing 1. Fetching page contents
import sys, time, os
from mechanize import Browser
LOGIN_URL = 'http://www.example.com/login'
USERNAME = 'DavidMertz'
PASSWORD = 'TheSpanishInquisition'
SEARCH_URL = 'http://www.example.com/search?'
FIXED_QUERY = 'food=spam&' 'utensil=spork&' 'date=the_future&'
VARIABLE_QUERY = ['actor=%s' % actor for actor in
('Graham Chapman',
'John Cleese',
'Terry Gilliam',
'Eric Idle',
'Terry Jones',
'Michael Palin')]
def fetch():
result_no = 0 # Number the output files
br = Browser() # Create a browser
br.open(LOGIN_URL) # Open the login page
br.select_form(name="login") # Find the login form
br['username'] = USERNAME # Set the form values
br['password'] = PASSWORD
resp = br.submit() # Submit the form
# Automatic redirect sometimes fails, follow manually when needed
if 'Redirecting' in br.title():
resp = br.follow_link(text_regex='click here')
# Loop through the searches, keeping fixed query parameters
for actor in in VARIABLE_QUERY:
# I like to watch what's happening in the console
print >> sys.stderr, '***', actor
# Lets do the actual query now
br.open(SEARCH_URL + FIXED_QUERY + actor)
# The query actually gives us links to the content pages we like,
# but there are some other links on the page that we ignore
nice_links = [l for l in br.links()
if 'good_path' in l.url
and 'credential' in l.url]
if not nice_links: # Maybe the relevant results are empty
break
for link in nice_links:
try:
response = br.follow_link(link)
# More console reporting on title of followed link page
print >> sys.stderr, br.title()
# Increment output filenames, open and write the file
result_no += 1
out = open(result_%04d' % result_no, 'w')
print >> out, response.read()
out.close()
# Nothing ever goes perfectly, ignore if we do not get page
except mechanize._response.httperror_seek_wrapper:
print >> sys.stderr, "Response error (probably 404)"
# Let's not hammer the site too much between fetches
time.sleep(1)
|
Having done my interactive exploration of the site of interest, I find that queries I wish to perform have some fixed elements and some variable elements. I just concatenate those together into a big GET request and take a look at the "results" page. In turn, that list of results contains links to the resources I actually want. So, I follow those (with a couple of try/except blocks thrown in, in case something does not work along the way) and save whatever I find on those content pages.
Pretty simple, huh? Mechanize can do more than this, but this short example shows you a broad brush of its capabilities.
At this point, we are done with mechanize; all that is left is to make some sense of that big bunch of HTML files we saved during the fetch() loop. The batch nature of the process lets me separate these cleanly, but obviously in a different program, fetch() and process() might interact more closely. Beautiful Soup makes the post-processing even easier than the initial fetch.
For this batch task, we want to produce tabular comma-separated value (CSV) data from some bits and pieces we find on those various Web pages we fetched.
Listing 2. Making orderly data from odds and ends with Beautiful Soup
from glob import glob
from BeautifulSoup import BeautifulSoup
def process():
print "!MOVIE,DIRECTOR,KEY_GRIP,THE_MOOSE"
for fname in glob('result_*'):
# Put that sloppy HTML into the soup
soup = BeautifulSoup(open(fname))
# Try to find the fields we want, but default to unknown values
try:
movie = soup.findAll('span', {'class':'movie_title'})[1].contents[0]
except IndexError:
fname = "UNKNOWN"
try:
director = soup.findAll('div', {'class':'director'})[1].contents[0]
except IndexError:
lname = "UNKNOWN"
try:
# Maybe multiple grips listed, key one should be in there
grips = soup.findAll('p', {'id':'grip'})[0]
grips = " ".join(grips.split()) # Normalize extra spaces
except IndexError:
title = "UNKNOWN"
try:
# Hide some stuff in the HTML <meta> tags
moose = soup.findAll('meta', {'name':'shibboleth'})[0]['content']
except IndexError:
moose = "UNKNOWN"
print '"%s","%s","%s","%s"' % (movie, director, grips, moose)
|
The code here in process() is an impressionistic first look at Beautiful Soup. Readers should read its documentation to find more on the module details, but the general feel is well represented in this snippet. Most soup code consists of some .findAll() calls into a page that might be only approximately well-formed HTML. Thrown in here are some DOM-like .parent, nextSibling, and previousSibling attributes. These are akin to the "quirks" mode of Web browsers. What we find in the soup is not quite a parse tree; it is more like a sack full of the vegetables that might go in the soup (to strain a metaphor).
Old fogies like me, and even some younger readers, will remember the great delight of scripting with TCL Expect (or with its workalikes written in Python and many other languages). Automating interaction with shells, including remote ones such as telnet, ftp, ssh, and the like, is relatively straightforward since everything is displayed in the session. Web interaction is slightly more subtle in that information is divided between headers and bodies, and various dependent resources are often bundled together with href links, frames, Ajax, and so on. In principle, however, you could just use a tool like wget to retrieve every byte a Web server might provide, and then run the very same style of Expect scripts as with other connection protocols.
In practice, few programmers are quite so committed to old-timey approaches as my suggested wget + Expect approach. Mechanize still has much of the same familiar and comforting feel as those nice Expect scripts, and is just as easy to write, if not easier. The Browser() object commands such as .select_form(), .submit(), and .follow_link() are really just the simplest and most obvious way of saying "look for this and send that" while bundling in all the niceness of sophisticated state and session handling that we would want in a Web automation framework.
Learn
- "Build a Web spider on Linux" (developerWorks, November 2006) discusses Web spiders and scrapers and shows how to build several simple scrapers using Ruby.
- "Debug and tune applications on the fly with Firebug" (developerWorks, May 2008) shows how to use Firebug to go far beyond viewing the page source for Web and Ajax applications.
- "Using Net-SNMP and IPython" (developerWorks, December 2007) details how IPython and Net-SNMP can combine to provide interactive, Python-based network management.
- In the developerWorks Linux zone, find more resources for Linux developers, and scan our most popular articles and tutorials.
- See all Linux tips and Linux tutorials on developerWorks.
- Stay current with developerWorks technical events and Webcasts.
Get products and technologies
- Download mechanize and its documentation.
- Download Beautiful Soup and its documentation.
- IPython is a wonderfully enhanced version of Python's native interactive shell that can do some rather fancy things such as aiding parallelizing computations; I mostly use it simply for its interactivity aids such as colorization of code, improved command-line recall, tab completion, macro capabilities, and improved interactive help.
- You can install Firebug, which delivers a wealth of editing, debugging, and monitoring Web development tools at your fingertips while you browse, right from the Tools/Add-ons menu of Firefox 3.0+. You can add the Web Developer extension, which adds a menu and a toolbar to the browser with various Web developer tools, the same way.
- With IBM trial software, available for download directly from developerWorks, build your next development project on Linux.
Discuss
- Participate in the discussion forum.
- Get involved in the My developerWorks community; with your personal profile and custom home page, you can tailor developerWorks to your interests and interact with other developerWorks users.
David Mertz has been writing the developerWorks columns Charming Python and XML Matters since 2000. Check out his book Text Processing in Python. For more on David, see his personal Web page.
Charming Python: Mechanize와
Beautiful Soup을 이용한 손쉬운 웹 데이터 수집
웹 사이트 데이터를 손쉽게 추출하고 관리할 수 있는 Python 도구
요약: 웹 페이지의 데이터를 수집하기 위해 mechanize 라이브러리를 사용하면 웹 사이트와의 스크래핑 및 상호 작용을 자동화할 수 있습니다. Mechanize에서는 양식을 채우고 쿠키를 설정 및 저장할 수 있으며 간단한 다른 도구를 통해 Python 스크립트를 대화식 웹 사이트의 정식 웹 브라우저처럼 보이게 만들 수 있습니다. 함께 자주 사용되는 도구인 Beautiful Soup은 Python 프로그램에서 웹 사이트에 자주 포함되어 있는 복잡한 "거의 대부분의 HTML"을 손쉽게 파악하는 데 유용합니다.
웹 사이트와 상호 작용하는 스크립트는 기본적인 Python 모듈만으로도 작성할 수 있다. 하지만 필요하지 않다면 원하지도 않는다. Python 2.x의urllib 및 urllib2 모듈과 Python 3.0의 통합 urllib.* 서브패키지를 사용하면 일반적인 URL에 있는 자원을 무난하게 가져올 수 있다. 하지만 웹 페이지에서 찾은 내용에 대해 상당히 정교한 상호 작용을 수행하려면mechanize 라이브러리(참고자료의 다운로드 링크 참조)가 실제로 필요하다.
웹 스크래핑을 자동화하거나 웹 사이트와의 사용자 상호 작용의 시뮬레이션을 자동화할 때 어려운 작업 중 하나는 서버에서 쿠키를 사용하여 세션 진행상황을 추적하는 것이다. 분명 쿠키는 HTTP 헤더의 일부이며 urllib가 자원을 열 때 기본적으로 표시된다. 게다가 표준 모듈인 Cookie(Python 3의 http.cookie)와 cookielib(Python 3의http.cookiejar)를 사용하면 원시 텍스트 처리보다 높은 상위 레벨에서 그러한 헤더를 처리할 수 있다. 그렇다고 하더라도 이 레벨에서 이 처리를 수행하는 작업은 필요 이상으로 복잡하다. Mechanize 라이브러리를 사용하면 이 작업을 더 높은 추상화 레벨에서 처리할 수 있으며, 스크립트 또는 대화식 Python 쉘이 실제 웹 브라우저와 거의 유사하게 작동한다.
Python의 mechanize는 유사한 기능을 제공하는 Perl의 WWW:Mechanize에서 아이디어를 얻어 개발되었다. 물론 오래동안 Python을 애용해 온 필자가 보기에는 두 언어의 일반 패턴을 따르는 mechanize가 좀 더 강력하다.
Mechanize의 절친한 친구로 매우 뛰어난 라이브러리인 Beautiful Soup(참고자료의 다운로드 링크 참조)이 있다. 이 라이브러리는 실제 웹 페이지에서 자주 볼 수 있는 대체로 유효한 HTML에 적합한 "엉성한 구문 분석기"이다. Beautiful Soup과 mechanize를 반드시함께 사용할 필요는 없지만 "실제로 존재하는 웹"과 상호 작용하게 되면 두 도구를 함께 사용하는 경우가 그렇지 않은 경우보다 많을 것이다.
필자는 몇몇 프로그래밍 프로젝트에서 mechanize를 사용해 보았으며 최근 프로젝트에서는 유명한 웹 사이트에서 기준을 충족하는 이름 목록을 수집했었다. 이 사이트에는 몇 가지 검색 기능이 있었지만 그러한 검색을 수행할 수 있는 공식 API는 없었다. 이 기사를 보면서 필자의 작업 내용을 구체적으로 추측할 수 있을 것이므로 이 기사에서는 스크래핑된 사이트나 고객에 대한 정보를 과도하게 제공하는 것을 피하기 위해 세부 사항이 변경된 코드를 예제로 제공할 것이다. 일반적으로 이 기사에서 제공하는 것과 매우 유사한 코드는 유사한 태스크에 공통으로 사용된다.
실제로 웹 스크래핑/분석 코드를 개발하는 프로세스에서 필자는 관련된 웹 페이지에서 실제로 발생하는 작업을 확인하기 위해 웹 페이지의 내용을 대화식으로 살펴보고 실험해 볼 수 있는 능력이 매우 중요하다는 것을 알게 되었다. 일반적으로 이러한 페이지는 한 사이트 내의 페이지 세트로서 쿼리를 통해 동적으로 생성되거나(하지만 일관된 패턴을 가지고 있음) 상당히 엄격한 템플리트에 따라 미리 생성된 페이지이다.
이 대화식 실험을 효과적으로 수행할 수 있는 한 가지 방법은 Python 쉘 내에서 특히, IPython(참고자료의 링크 참조)과 같이 향상된 쉘 내에서 mechanize 자체를 사용하는 것이다. 이 방법으로 탐험을 수행하면 프로덕션 환경에서 원하는 상호 작용을 수행하는 최종 스크립트를 작성하기 전에 다양한 링크 자원을 요청하고, 양식을 제출하고, 사이트 쿠키를 관리 또는 조작하는 등의 작업을 미리 수행해 볼 수 있다.
하지만 웹 사이트와의 상호 작용을 실험해 본 결과 실제 최신 웹 브라우저에서 더 좋은 성과를 얻을 수 있었다. 보기 좋게 변환된 페이지를 보면 지정된 페이지나 양식에서 수행 중인 작업을 훨씬 더 빨리 이해할 수 있다. 문제는 페이지 변환이 전체 작업의 절반에도 미치지 못한다는 것이다. "페이지 소스"를 가지고 있으면 좀 더 낫다. 지정된 웹 페이지 뒤에서 이루어지는 작업이나 웹 서버와 주고 받는 일련의 상호 작용을 실제로 이해하려면 더 많은 것이 필요하다.
이러한 정보를 얻기 위해 필자는 일반적으로 Firebug(참고자료의 링크 참조)나 Firefox용 Web Developer 플러그인을 사용한다. (또는 최신 Safari 버전의 내장 옵션인 Develop 메뉴를 사용하기도 하지만 이 메뉴는 다른 대상을 위한 것이다.) 이러한 모든 도구는 양식 필드를 확인하고, 비밀번호를 보고, 페이지의 DOM을 검사하고, Javascript를 살펴보거나 실행하고, Ajax 트래픽을 검사하는 등의 작업을 수행하는 데 유용하다. 이러한 도구의 장점 및 하위 호환성 비교는 또 하나의 기사로 다룰 만큼 복잡하므로 여기에서는 다루지 않겠지만 웹 지향 프로그래밍을 수행하고 있다면 이러한 도구에 익숙해 지는 것이 좋다.
상호 작용을 자동화하려는 웹 사이트를 실험하는 데 사용하는 도구가 무엇이든지 간에 태스크를 수행하는 데 필요한 놀랄 만큼 간결한 mechanize 코드를 작성하는 것보다 사이트의 실제 작업을 파악하는 데 더 많은 시간이 소요될 것이다.
위에서 언급한 프로젝트의 목적에 따라 수백 줄의 스크립트를 다음 두 가지 기능으로 나누었다.
- 관심을 가지고 있는 모든 결과 검색
- 검색된 페이지에서 흥미로운 정보 추출
필자는 개발 편의를 고려하여 스크립트를 구성했으며, 태스크를 시작할 때 이러한 두 기능을 하나하나 수행하는 방법을 해결할 필요가 있다는 것을 알고 있었다. 원하는 정보가 일반적인 페이지 콜렉션에 있다는 생각만 했을 뿐 페이지의 구체적인 레이아웃을 아직 조사하지 않았다.
우선 페이지를 가져와서 디스크에 저장해 두었다. 이제 저장된 파일에서 원하는 정보를 추출하는 태스크를 수행할 수 있다. 물론 검색된 정보를 사용하여 동일한 세션 내에서 새로운 상호 작용을 공식화해야 한다면 약간 다른 개발 단계 시퀀스를 사용해야 한다.
이제 fetch() 함수를 살펴보자.
목록 1. 페이지 내용 가져오기
import sys, time, os
from mechanize import Browser
LOGIN_URL = 'http://www.example.com/login'
USERNAME = 'DavidMertz'
PASSWORD = 'TheSpanishInquisition'
SEARCH_URL = 'http://www.example.com/search?'
FIXED_QUERY = 'food=spam&' 'utensil=spork&' 'date=the_future&'
VARIABLE_QUERY = ['actor=%s' % actor for actor in
('Graham Chapman',
'John Cleese',
'Terry Gilliam',
'Eric Idle',
'Terry Jones',
'Michael Palin')]
def fetch():
result_no = 0 # Number the output files
br = Browser() # Create a browser
br.open(LOGIN_URL) # Open the login page
br.select_form(name="login") # Find the login form
br['username'] = USERNAME # Set the form values
br['password'] = PASSWORD
resp = br.submit() # Submit the form
# Automatic redirect sometimes fails, follow manually when needed
if 'Redirecting' in br.title():
resp = br.follow_link(text_regex='click here')
# Loop through the searches, keeping fixed query parameters
for actor in in VARIABLE_QUERY:
# I like to watch what's happening in the console
print >> sys.stderr, '***', actor
# Lets do the actual query now
br.open(SEARCH_URL + FIXED_QUERY + actor)
# The query actually gives us links to the content pages we like,
# but there are some other links on the page that we ignore
nice_links = [l for l in br.links()
if 'good_path' in l.url
and 'credential' in l.url]
if not nice_links: # Maybe the relevant results are empty
break
for link in nice_links:
try:
response = br.follow_link(link)
# More console reporting on title of followed link page
print >> sys.stderr, br.title()
# Increment output filenames, open and write the file
result_no += 1
out = open(result_%04d' % result_no, 'w')
print >> out, response.read()
out.close()
# Nothing ever goes perfectly, ignore if we do not get page
except mechanize._response.httperror_seek_wrapper:
print >> sys.stderr, "Response error (probably 404)"
# Let's not hammer the site too much between fetches
time.sleep(1)
|
관심 있는 사이트에 대한 대화식 탐색을 마치면 수행하려는 쿼리에 몇 가지 고정 요소와 가변 요소가 있다는 것을 알 수 있다. 그러한 요소를 하나의 큰 GET 요청으로 연결하고 "결과" 페이지를 살펴본다. 그러면 이 결과 목록에 실제로 원하는 자원에 대한 링크가 들어 있다. 이제 그러한 링크를 따라가서(오류가 있을 경우 예외가 발생하는 한 쌍의 try/except 블록 사용) 해당 페이지에서 검색된 모든 내용을 저장한다.
정말 간단하다. Mechanize를 통해 이 보다 더 많은 작업을 수행할 수 있지만 이 짧은 예제에서는 그 기능을 개략적으로 보여 준다.
이제 mechanize 작업을 모두 완료했으며 앞으로는 fetch() 루프 동안 저장된 수많은 HTML 파일을 처리하는 작업이 남아 있다. 프로세스의 일괄처리 특성을 고려하여 이러한 기능을 명확하게 분리하기는 했지만 분명 다른 프로그램에서는 fetch()와 process()가 좀 더 긴밀하게 상호 작용할 것이다. Beautiful Soup을 통해 수행할 사후 처리는 초기 페치보다 훨씬 쉽다.
이 일괄처리 태스크에서는 가져온 다양한 웹 페이지에서 찾아낸 정보를 바탕으로 표 형식의 CSV(Comma-Separated Value)를 생성할 것이다.
목록 2. Beautiful Soup을 사용하여 어지러운 정보를 정리된 데이터로 만들기
from glob import glob
from BeautifulSoup import BeautifulSoup
def process():
print "!MOVIE,DIRECTOR,KEY_GRIP,THE_MOOSE"
for fname in glob('result_*'):
# Put that sloppy HTML into the soup
soup = BeautifulSoup(open(fname))
# Try to find the fields we want, but default to unknown values
try:
movie = soup.findAll('span', {'class':'movie_title'})[1].contents[0]
except IndexError:
fname = "UNKNOWN"
try:
director = soup.findAll('div', {'class':'director'})[1].contents[0]
except IndexError:
lname = "UNKNOWN"
try:
# Maybe multiple grips listed, key one should be in there
grips = soup.findAll('p', {'id':'grip'})[0]
grips = " ".join(grips.split()) # Normalize extra spaces
except IndexError:
title = "UNKNOWN"
try:
# Hide some stuff in the HTML <meta> tags
moose = soup.findAll('meta', {'name':'shibboleth'})[0]['content']
except IndexError:
moose = "UNKNOWN"
print '"%s","%s","%s","%s"' % (movie, director, grips, moose)
|
process()의 코드를 보면 Beautiful Soup에 대한 첫 인상이 강렬하게 남을 것이다. 모듈 세부사항을 자세히 살펴보려면 해당 문서를 읽어야 한다. 하지만 이 스니펫에도 일반적인 느낌이 잘 표현되어 있다. 대부분의 soup 코드는 대체로 잘 짜여져 있는 하나의 HTML 페이지에 몇 개의 .findAll() 호출이 들어 있는 형태로 구성되어 있다. 여기에서는 DOM 형태의 .parent, nextSibling 및previousSibling 속성이 사용된다. 이러한 속성은 웹 브라우저의 "쿽스(quirks)" 모드와 유사하다. Soup에서 단순히 구문 분석 트리만 발견한 것이 아니라 수프에 들어갈 한 보따리의 야채를 발견한 것이다(은유적 표현).
필자처럼 나이든 사람과 일부 젊은 독자들은 TCL Expect(또는 Python을 비롯한 여러 언어로 작성된 동일한 기능의 제품)를 사용하여 스크립트를 작성하던 좋은 시절을 기억할 것이다. Telnet, ftp, ssh 등과 같은 원격 쉘을 포함한 쉘과의 상호 작용을 자동화하는 작업은 비교적 쉽다. 왜냐하면 세션에 모든 내용이 표시되기 때문이다. 웹 상호 작용의 경우에는 정보가 헤더와 본문으로 분리되어 있고 다양한 종속 자원이 href 링크, 프레임, Ajax 등과 함께 묶여 있는 경우가 많기 때문에 좀 더 복잡하다. 하지만 원칙적으로 보면 다른 연결 프로토콜과 마찬가지로 wget 등의 도구를 사용하여 웹 서버에서 제공하는 모든 바이트를 검색한 다음 동일한 유형의 Expect 스크립트를 실행할 수 있다.
실제로 필자가 제안한 wget + Expect 방법과 같은 구시대적인 방법에 헌신하는 프로그래머는 거의 없다. Mechanize는 뛰어난 Expect 스크립트와 같은 친숙함과 편안한 느낌을 가지고 있으며 Expect 스크립트보다 어려울지는 몰라도 쉽게 작성할 수 있다..select_form(), .submit() 및 .follow_link()와 같은 Browser() 오브젝트 명령은 실제로 웹 자동화 프레임워크에 필요한 모든 정교한 상태 및 세션 처리를 번들링하는 동안 "이것을 보고 전송하세요"라고 말하는 것과 같이 가장 단순하고 가장 명확한 방법이다.
교육
- "리눅스에서 웹 스파이더(Web spider) 구현하기"(developerWorks, 2006년 11월)에서는 Web spider와 스크래퍼에 대해 설명한 후 Ruby를 사용하여 여러 가지 간단한 스크래퍼를 빌드하는 방법을 보여 준다.
- "파이어버그를 이용한 신속한 웹 애플리케이션 디버깅과 튜닝"(developerWorks, 2008년 5월)에서는 파이어버그를 사용하여 웹 및 Ajax 애플리케이션의 페이지 소스를 보고 분석하는 방법에 대해 설명한다.
- "Using Net-SNMP and IPython"(developerWorks, 2007년 12월)에서는 IPython과 Net-SNMP를 결합하여 대화식 Python 기반 네트워크 관리를 제공하는 방법에 대해 설명한다.
- developerWorks Linux 영역에서는 Linux 개발자에게 도움이 되는 여러 가지 리소스를 제공하고 있으며 가장 인기 있는 기사와 튜토리얼도 볼 수 있다.
- developerWorks에 있는 Linux 팁과 Linux 튜토리얼을 모두 볼 수 있다.
- developerWorks 기술 행사 및 웹 캐스트를 통해 최신 정보를 얻을 수 있다.
제품 및 기술 얻기
- Mechanize와 해당 문서를 다운로드할 수 있다.
- Beautiful Soup과 해당 문서를 다운로드할 수 있다.
- IPython은 아주 훌륭하게 향상된 Python의 네이티브 대화식 작업 쉘로 병렬 계산 지원과 같은 멋진 작업을 수행할 수 있다. 필자의 경우에는 대부분 코드 색 지정, 향상된 명령행 리콜, 자동 완성, 매크로 기능 및 향상된 대화식 도움말과 같은 대화식 작업 지원을 위해 이 쉘을 사용하고 있다.
- Firefox 3.0+의 Tools/Add-ons 메뉴를 통해 브라우저에서 직접 웹 개발 도구를 편집, 디버깅 및 모니터링하는 데 필요한 기능을 제공하는 Firebug를 설치할 수 있다. Web Developer extension을 추가할 수 있다. 이 확장은 다양한 Web developer 도구와 함께 메뉴 및 도구 모음을 브라우저에 추가한다.
- developerWorks에서 직접 다운로드할 수 있는 IBM 시험판 소프트웨어를 사용하여 Linux와 관련된 후속 개발 프로젝트를 구현해 볼 수 있다.
토론
- 포럼에 참여하기.
- 사용자의 개인 프로파일과 사용자 정의 홈 페이지가 제공되는 My developerWorks community에서는 관심을 가지고 있는 developerWorks의 여러 주제를 추적할 수 있으며 다른 developerWorks 사용자들과 의견을 나눌 수도 있다.
David Mertz has been writing the developerWorks columns Charming Python and XML Matters since 2000. Check out his bookText Processing in Python. For more on David, see his personal Web page.
광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.
댓글 0
| 번호 | 제목 | 글쓴이 | 날짜 | 조회 수 |
|---|---|---|---|---|
| 3 |
wxpython_in_action_wxact.pdf
| 졸리운_곰 | 2014.10.14 | 7113 |
| 2 |
wxPython 2.8 Application Development Cookbook (2010).pdf
| 졸리운_곰 | 2014.10.14 | 2773 |
| 1 |
The wxPython tutorial.pdf
| 졸리운_곰 | 2014.10.14 | 2875 |

