58

I want get captcha image from browser. I have got a url of this picture, but the this picture changes each updated time (url is constant).

Is there any solution to get picture from browser (like 'save picture as' button)?

From the other hand, I think it should be work:

  1. get screenshot of the browser
  2. get position of picture
  3. crop captcha from screenshot using opencv

link of the dynamic capcha - link

The problem was solved via screenshot:

browser.save_screenshot('screenshot.png')
img = browser.find_element_by_xpath('//*[@id="cryptogram"]')
loc = img.location

image = cv.LoadImage('screenshot.png', True)
out = cv.CreateImage((150,60), image.depth, 3)
cv.SetImageROI(image, (loc['x'],loc['y'],150,60))
cv.Resize(image, out)
cv.SaveImage('out.jpg', out)

Thanks

alecxe
  • 462,703
  • 120
  • 1,088
  • 1,195
user1941407
  • 2,722
  • 4
  • 27
  • 39
  • do you want a snapshot of the browser or a certain image from the page – Serial Jun 28 '13 at 09:46
  • I need captcha image from the page. Snapshoot is a way to get it. – user1941407 Jun 28 '13 at 13:55
  • what is the module 'cv' ?, nevermind--see: opencv – brizz Jan 03 '15 at 01:47
  • 1
    @erm3nda, apparently you don't know how to read past commas or understand dates. – brizz Jun 08 '15 at 22:21
  • What i don't know is the need of put here "hey i don't know X when you can search it instead". What's your real profit of doing that? Please, watch my next comment and say me what do you think about. – m3nda Jun 08 '15 at 22:27
  • whats "find_element_by_xpath"? nevermind-see: find_element_by_xpath. – m3nda Jun 08 '15 at 22:27
  • And 'cv' commas content is assumed to be the words that the user used when created the opencv instance. The same thing when you create a new Selenium webdriver object. – m3nda Jun 08 '15 at 22:30
  • Check this https://stackoverflow.com/a/54986819/7484554 – Super Mario Mar 04 '19 at 16:38

9 Answers9

69

Here's a complete example (using google's recaptcha as a target):

import urllib
from selenium import webdriver

driver = webdriver.Firefox()
driver.get('http://www.google.com/recaptcha/demo/recaptcha')

# get the image source
img = driver.find_element_by_xpath('//div[@id="recaptcha_image"]/img')
src = img.get_attribute('src')

# download the image
urllib.urlretrieve(src, "captcha.png")

driver.close()

UPDATE:

The problem with dynamic generated images is that there is a new image generated each time you request it. In that case, you have several options:

  • take a screenshot

     from selenium import webdriver
    
     driver = webdriver.Firefox()
     driver.get('https://moscowsg.megafon.ru/ps/scc/php/cryptographp.php?PHPSESSID=mfc540jkbeme81qjvh5t0v0bnjdr7oc6&ref=114&w=150')
    
     driver.save_screenshot("screenshot.png")
    
     driver.close()
    
  • simulate right click + "Save As". See this thread for more info.

starball
  • 20,030
  • 7
  • 43
  • 238
alecxe
  • 462,703
  • 120
  • 1,088
  • 1,195
  • 1
    google recapcha has static url. [link](http://www.google.com/recaptcha/api/image?c=03AHJ_Vusche6ZKC-el4YRHCYA97fvqPZYG8P11X3G4_ZsTSzOA0dEjCJil0oipRTTg88RedIGzvPjTqy1Ufq2s7nEs7u_2ZIJUE5uyzZevtCXqFDJTvMwZZGS8gUqkiDQgvFhNqvjkdRumsGuJwvBOujpEZcUSS62kLGFxYRI1puGd4EvrpCV3yg) – user1941407 Jun 28 '13 at 10:36
  • Then, give a link where it's dynamic. – alecxe Jun 28 '13 at 10:36
  • 1
    link of the capcha - [link](https://moscowsg.megafon.ru/ps/scc/php/cryptographp.php?PHPSESSID=mfc540jkbeme81qjvh5t0v0bnjdr7oc6&ref=114&w=150) – user1941407 Jun 28 '13 at 10:38
  • Additionally, could you provide a link to the whole page with captcha? – alecxe Jun 28 '13 at 11:01
  • [link](https://moscowsg.megafon.ru/) I save problem via screenshot. It is not efficient, but works. See head question – user1941407 Jun 28 '13 at 16:38
  • @alecxe,thanks for your post, i simulated your code and it worked for me, can you please tell me how did figure out the xpath for the image ('//div[@id="recaptcha_image"]/img') , thanks alot – fooBar Sep 12 '13 at 13:51
  • 15
    In the last couple of years.. urllib has changed. Instead of using urllib.urlretrieve, you now need to use urllib.request.urlretrieve. – zwep Jul 24 '17 at 06:40
  • 5
    can selenium itself download the image in the same session inside the automated browser? – Rahul Bali Jan 14 '18 at 20:21
  • to use urllib.request.urlretrieve first import urllib.request otherwise it will throw error – Adarsh Patel Feb 06 '20 at 22:16
  • 5
    this will _not_ work if accessing the image requires authentication – ccpizza Mar 10 '22 at 21:50
33

It's ok to save a screenshot from the whole page and then cut the image from, but you can also to use the "find" method from "webdriver" to locate the image you want to save, and write the "screenshot_as_png" property like below:

from selenium import webdriver
driver = webdriver.Firefox()
driver.get('https://www.webpagetest.org/')
with open('filename.png', 'wb') as file:
    file.write(driver.find_element_by_xpath('/html/body/div[1]/div[5]/div[2]/table[1]/tbody/tr/td[1]/a/div').screenshot_as_png)

Sometimes it could get an error because of the scroll, but depending on your necessity, it's a good way to get the image.

Suraj Kumar
  • 5,547
  • 8
  • 20
  • 42
Ramon
  • 339
  • 3
  • 3
  • @ Ramon this does not work, I am trying to get the image of your profile pic from this page and get the error `elenium.common.exceptions.WebDriverException: Message: unknown command: session/5734e4b0f8d6171317af42ddf0979562/element/0.9586500909174849-1/screenshot` – Jortega Jan 20 '20 at 21:32
  • So, @Jortega. Try sending your code here, then I can check if I find same mistake. – Ramon Jan 22 '20 at 12:09
  • But what if the image is a gif? Is there anyway get download that gif file? – oeter May 27 '22 at 16:17
6

The problem of using save_screenshot is that we cannot save an image in its original quality and cannot restore the alpha channel in an image. Therefore, I propose another solution. Here is a complete example using the selenium-wire library suggested by @codam_hsmits. It is possible to download images via ChromeDriver.

I have defined the following function to parse each request and save the request body to a file when necessary.

from seleniumwire import webdriver  # Import from seleniumwire
from urllib.parse import urlparse
import os
from mimetypes import guess_extension
import time
import datetime

def download_assets(requests,
                   asset_dir="temp",
                   default_fname="unnamed",
                   skip_domains=["facebook", "google", "yahoo", "agkn", "2mdn"],
                   exts=[".png", ".jpeg", ".jpg", ".svg", ".gif", ".pdf", ".bmp", ".webp", ".ico"],
                   append_ext=False):
    asset_list = {}
    for req_idx, request in enumerate(requests):
        # request.headers
        # request.response.body is the raw response body in bytes
        if request is None or request.response is None or request.response.headers is None or 'Content-Type' not in request.response.headers:
            continue
            
        ext = guess_extension(request.response.headers['Content-Type'].split(';')[0].strip())
        if ext is None or ext == "" or ext not in exts:
            #Don't know the file extention, or not in the whitelist
            continue
        parsed_url = urlparse(request.url)
        
        skip = False
        for d in skip_domains:
            if d in parsed_url.netloc:
                skip = True
                break
        if skip:
            continue
        
        frelpath = parsed_url.path.strip()
        if frelpath == "":
            timestamp = str(datetime.datetime.now().replace(microsecond=0).isoformat())
            frelpath = f"{default_fname}_{req_idx}_{timestamp}{ext}"
        elif frelpath.endswith("\\") or frelpath.endswith("/"):
            timestamp = str(datetime.datetime.now().replace(microsecond=0).isoformat())
            frelpath = frelpath + f"{default_fname}_{req_idx}_{timestamp}{ext}"
        elif append_ext and not frelpath.endswith(ext):
            frelpath = frelpath + f"_{default_fname}{ext}" #Missing file extension but may not be a problem
        if frelpath.startswith("\\") or frelpath.startswith("/"):
            frelpath = frelpath[1:]
        
        fpath = os.path.join(asset_dir, parsed_url.netloc, frelpath)
        if os.path.isfile(fpath):
            continue
        os.makedirs(os.path.dirname(fpath), exist_ok=True)
        print(f"Downloading {request.url} to {fpath}")
        asset_list[fpath] = request.url
        try:
            with open(fpath, "wb") as file:
                file.write(request.response.body)
        except:
            print(f"Cannot download {request.url} to {fpath}")
    return asset_list

Let's download some images from Google homepage to temp folder.

# Create a new instance of the Chrome/Firefox driver
driver = webdriver.Chrome()

# Go to the Google home page
driver.get('https://www.google.com')

# Download content to temp folder
asset_dir = "temp"

while True:
    # Please browser the internet, it will collect the images for every second
    time.sleep(1)
    download_assets(driver.requests, asset_dir=asset_dir)

driver.close()

Note that it cannot decide which images can be seen on the page rather than being hidden in the background, so the users should actively click the buttons or links to trigger new download requests.

Paco Wong
  • 680
  • 5
  • 12
3

So, just to stay relevant, here is a 2020 solution using seleniumwire, which is a package that gives you access to the requests made in a browser. You can use it easily as follows:

from seleniumwire import webdriver

# Sometimes, selenium randomly crashed when using seleniumwire, these options fixed that.
# Probably has to do with how it proxies everything.
options.add_argument('--ignore-certificate-errors')
options.add_argument('--ignore-ssl-errors')

driver = webdriver.Chrome(chrome_options=options)
driver.get("https://google.com")

for request in driver.requests:
    # request.path
    # request.method
    # request.headers
    # request.response is the response instance
    # request.response.body is the raw response body in bytes

    # if you are using it for a ton of requests, make sure to clear them:
    del driver.requests

Now, why would you need this? Well, for example for ReCaptcha bypassing, or for bypassing something like Incapsula. Use this at your own risk.

Harm Smits
  • 437
  • 3
  • 10
1

Use the following code to download the image

from selenium import webdriver
#set chromedriver.exe path
driver = webdriver.Chrome()
driver.implicitly_wait(0.5)
#maximize browser
driver.maximize_window()
#launch URL
driver.get("name of webpage from where you want to download image");
#open file in write and binary mode
with open('Logo.png', 'wb') as file:
#identify image to be captured
   l = driver.find_element_by_xpath('//*[@alt="name in the alt of image"]')
#write file
   file.write(l.screenshot_as_png)
#close browser
driver.quit()
1

You can download images without losing quality with JS:

from io import BytesIO
from PIL import Image
from base64 import b64decode

driver.get(url)

# Create a canvas, set it's width and height equal to image's
# Write image to canvas, translate to base64
# Remove metadata prefix
b64img = driver.execute_script(r'''
var img = document.getElementsByTagName("img")[0];
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
var dataURL = canvas.toDataURL("image/png");
return dataURL.replace(/^data:image\/(png|jpg);base64,/, "");
''')

# Decode from base64, translate to bytes and write to PIL image
img = Image.open(BytesIO(b64decode(b64img)))
Limtis
  • 95
  • 7
  • Is a valid solution but this will fail in most of the cases: It will throw you this error https://stackoverflow.com/questions/22710627/tainted-canvases-may-not-be-exported – titusfx Nov 04 '22 at 10:51
0

If you need authorization to download an image, I found the best solution is to combine selenium and selenium-requests:

  1. Use selenium to log in to the website
  2. Parse website to get the image url
  3. Use selenium-requests to get the image using code similar to
response = driver.request("GET", image_url, stream=True)
response.raise_for_status()
with open(path, 'wb') as f:
    response.raw.decode_content = True
    shutil.copyfileobj(response.raw, f) 

as per How to download image using requests

0

refer to 'disable_encoding': True

from seleniumwire import webdriver

seleniumwire_options = {
    "disable_encoding": True,
}

driver = webdriver.Chrome(options=options, seleniumwire_options=seleniumwire_options)
driver.get(url) # direct url to image

request = driver.last_request

file = url.split("/")[-1]
with open(file, "wb") as f:
    f.write(request.response.body)
foo
  • 1
  • 1
-1

Here it is.

  • open the image with Selenium WebDriver
  • extract the width and the height of the image with BeautifulSoup
  • set the right current window size with driver.set_window_size, and take a screenshot with driver.save_screenshot
from bs4 import BeautifulSoup
from selenium import webdriver
 
import os
from urllib.parse import urlparse
 
url = 'https://image.rakuten.co.jp/azu-kobe/cabinet/hair1/hb-30-pp1.jpg'
 
filename = os.path.basename(urlparse(url).path)
filename_png = os.path.splitext(filename)[0] + '.png'  # change file extension to .png
 
opts = webdriver.ChromeOptions()
opts.headless = True
driver = webdriver.Chrome(options=opts)
 
driver.get(url)
 
# Get the width and height of the image
soup = BeautifulSoup(driver.page_source, 'lxml')
width = soup.find('img')['width']
height = soup.find('img')['height']
 
driver.set_window_size(width, height) # driver.set_window_size(int(width), int(height))
driver.save_screenshot(filename_png)

It also works for Google's image format, WebP.

Refer to Downlolad Google’s WebP Images via Take Screenshots with Selenium WebDriver.

SparkAndShine
  • 17,001
  • 22
  • 90
  • 134